home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 151 / cd-rom 151.iso / internet / firefox / Firefox Setup 3.0 Beta 1.exe / nonlocalized / components / nsSafebrowsingApplication.js < prev    next >
Encoding:
Text File  |  2007-11-09  |  100.2 KB  |  2,898 lines

  1. const Cc = Components.classes;
  2. const Ci = Components.interfaces;
  3.  
  4. // This is copied from toolkit/components/content/js/lang.js.
  5. // It seems cleaner to copy this rather than #include from so far away.
  6. Function.prototype.inherits = function(parentCtor) {
  7.   var tempCtor = function(){};
  8.   tempCtor.prototype = parentCtor.prototype;
  9.   this.superClass_ = parentCtor.prototype;
  10.   this.prototype = new tempCtor();
  11. }  
  12.  
  13. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\application.js"
  14.  
  15. // We instantiate this variable when we create the application.
  16. var gDataProvider = null;
  17.  
  18. // An instance of our application is a PROT_Application object. It
  19. // basically just populates a few globals and instantiates wardens,
  20. // the listmanager, and the about:blocked error page.
  21.  
  22. /**
  23.  * An instance of our application. There should be exactly one of these.
  24.  * 
  25.  * Note: This object should instantiated only at profile-after-change
  26.  * or later because the listmanager and the cryptokeymanager need to
  27.  * read and write data files. Additionally, NSS isn't loaded until
  28.  * some time around then (Moz bug #321024).
  29.  *
  30.  * @constructor
  31.  */
  32. function PROT_Application() {
  33.   this.debugZone= "application";
  34.  
  35. //@line 83 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\application.js"
  36.   
  37.   // expose some classes
  38.   this.PROT_Controller = PROT_Controller;
  39.   this.PROT_PhishingWarden = PROT_PhishingWarden;
  40.   this.PROT_MalwareWarden = PROT_MalwareWarden;
  41.  
  42.   // Load data provider pref values
  43.   gDataProvider = new PROT_DataProvider();
  44.  
  45.   // expose the object
  46.   this.wrappedJSObject = this;
  47. }
  48.  
  49. /**
  50.  * @param name String The type of url to get (either Phish or Error).
  51.  * @return String the report phishing URL (localized).
  52.  */
  53. PROT_Application.prototype.getReportURL = function(name) {
  54.   return gDataProvider["getReport" + name + "URL"]();
  55. }
  56.  
  57. /**
  58.  * about:blocked implementation
  59.  */
  60. PROT_Application.prototype.newChannel = function(uri) {
  61.   var ioService = Cc["@mozilla.org/network/io-service;1"]
  62.                  .getService(Ci.nsIIOService);
  63.   var childURI = ioService.newURI("chrome://browser/content/safebrowsing/blockedSite.xhtml",
  64.                                   null, null);
  65.   var channel = ioService.newChannelFromURI(childURI);
  66.   channel.originalURI = uri;
  67.  
  68.   return channel;
  69. }
  70.  
  71. PROT_Application.prototype.getURIFlags = function(uri) {
  72.   return Ci.nsIAboutModule.ALLOW_SCRIPT;
  73. }
  74.  
  75. PROT_Application.prototype.QueryInterface = function(iid) {
  76.   if (iid.equals(Ci.nsISupports) ||
  77.       iid.equals(Ci.nsIAboutModule))
  78.     return this;
  79.  
  80.   Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  81.   return null;
  82. }
  83. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\browser-view.js"
  84.  
  85. // There is one BrowserView per browser window, and each BrowserView
  86. // is responsible for keeping track of problems (phishy documents)
  87. // within that window. The BrowserView is also responsible for
  88. // figuring out what to do about such problems, for example, whether
  89. // the tab with a phishy page is currently showing and therefore if we
  90. // should be showing a warning.
  91. // 
  92. // The BrowserView receives information from three places:
  93. //
  94. // - from the phishing warden. When the phishing warden notices a
  95. //   problem, it queries all browser views to see which one (if any)
  96. //   has the Document that is problematic. It then hands the problem
  97. //   off to the appropriate BrowserView.
  98. // 
  99. // - from the controller. The controller responds to explicit user 
  100. //   actions (tab switches, requests to hide the warning message, 
  101. //   etc.) and let's the BrowserView know about any user action 
  102. //   having to do with the problems it is tracking.
  103. //
  104. // The BrowserView associates at most one "problem" with each Document
  105. // in the browser window. It keeps state about which Documents are 
  106. // problematic by storing a "problem queue" on each browser (tab).
  107. // At most one problematic document per browser (tab) is active
  108. // at any time. That is, we show the warning for at most one phishy
  109. // document at any one time. If another phishy doc loads in that tab,
  110. // it goes onto the end of the queue to be activated only when the
  111. // currently active document goes away.
  112. //
  113. // If we had multiple types of warnings (one for after the page had
  114. // loaded, one for when the user clicked a link, etc) here's where
  115. // we'd select the appropate one to use. As it stands, we only have
  116. // one displayer (an "afterload" displayer). A displayer knows _how_
  117. // to display a warning, whereas as the BrowserView knows _what_ and
  118. // _when_.
  119. //
  120. // To keep things (relatively) easy to reason about and efficient (the
  121. // phishwarden could be querying us inside a progresslistener
  122. // notification, or the controller inside an event handler), we have
  123. // the following rules:
  124. //
  125. // - at most one of a displayer's start() or stop() methods is called
  126. //   in any iteration (if calling two is required, the second is run in 
  127. //   the next event loop)
  128. // - displayers should run their operations synchronously so we don't have
  129. //   to look two places (here and in the displayer) to see what is happening 
  130. //   when
  131. // - displayer actions are run after cleaning up the browser view state
  132. //   in case they have consequences
  133. //
  134. // TODO: this could use some redesign, but I don't have time.
  135. // TODO: the queue needs to be abstracted, but we want another release fast,
  136. //       so I'm not going to touch it for the time being
  137. // TODO: IDN issues and canonical URLs?
  138. // TODO: Perhaps we should blur the page before showing a warning in order
  139. //       to prevent stray keystrokes?
  140.  
  141. /**
  142.  * The BrowerView is responsible for keeping track of and figuring out
  143.  * what to do with problems within a single browser window.
  144.  * 
  145.  * TODO 
  146.  * Unify all browser-related state here. Currently it's split
  147.  * between two objects, this object and the controller. We could have
  148.  * this object be solely responsible for UI hide/show decisions, which
  149.  * would probably make it easier to reason about what's going on.
  150.  * 
  151.  * TODO 
  152.  * Investigate an alternative model. For example, we could factor out
  153.  * the problem signaling stuff from the tab/UI logic into a
  154.  * ProblemRegistry. Attach listeners to new docs/requests as they go
  155.  * by and have these listeners periodically check in with a
  156.  * ProblemRegistry to see if they're watching a problematic
  157.  * doc/request. If so, then have them flag the browser view to be
  158.  * aware of the problem.
  159.  *
  160.  * @constructor
  161.  * @param tabBrowser Reference to the main tabbrowser we'll use to query 
  162.  *                   for information about active tabs/browsers.
  163.  */ 
  164. function PROT_BrowserView(tabBrowser) {
  165.   this.debugZone = "browserview";
  166.   this.tabBrowser_ = tabBrowser;
  167.   this.doc_ = this.tabBrowser_.ownerDocument;
  168. }
  169.  
  170. /**
  171.  * Invoked by the warden to give us the opportunity to handle a
  172.  * problem.  A problem is signaled once per request for a problem
  173.  * Document and is handled at most once, so there's no issue with us
  174.  * "losing" a problem due to multiple concurrently loading Documents
  175.  * with the same URL.
  176.  *
  177.  * @param warden Reference to the warden signalling the problem. We'll
  178.  *               need him to instantiate one of his warning displayers
  179.  * 
  180.  * @param request The nsIRequest that is problematic
  181.  *
  182.  * @returns Boolean indicating whether we handled problem
  183.  */
  184. PROT_BrowserView.prototype.tryToHandleProblemRequest = function(warden,
  185.                                                                 request) {
  186.   // XXX: pass around the URL instead of the request.  request.name isn't
  187.   // really supposed to be used and isn't guaranteed to give the URL.
  188.   var url = request.name;
  189.   var browsers = this.tabBrowser_.browsers;
  190.   for (var i = 0; i < browsers.length; i++) {
  191.     var browser = browsers[i];
  192.     var doc = browser.contentDocument;
  193.  
  194.     // We only care about top level documents (and not about frames)
  195.     if (doc.location.href == url && !this.getProblem_(doc, browser)) {
  196.       this.isProblemDocument_(browser, doc, warden);
  197.       return true;
  198.     }
  199.   }
  200.   return false;
  201. }
  202.  
  203. /**
  204.  * We're sure a particular Document is problematic, so let's instantiate
  205.  * a dispalyer for it and add it to the problem queue for the browser.
  206.  *
  207.  * @param browser Reference to the browser in which the problem doc resides
  208.  *
  209.  * @param doc Reference to the problematic document
  210.  * 
  211.  * @param warden Reference to the warden signalling the problem.
  212.  */
  213. PROT_BrowserView.prototype.isProblemDocument_ = function(browser, 
  214.                                                          doc, 
  215.                                                          warden) {
  216.  
  217.   G_Debug(this, "Document is problem: " + doc.location.href);
  218.  
  219.   var url = doc.location.href;
  220.  
  221.   // We only have one type of displayer right now
  222.   var displayer = new warden.displayers_["afterload"]("Phishing afterload",
  223.                                                       browser,
  224.                                                       this.doc_,
  225.                                                       url);
  226.  
  227.   // We listen for the problematic document being navigated away from
  228.   // so we can remove it from the problem queue
  229.  
  230.   var hideHandler = BindToObject(this.onNavAwayFromProblem_, 
  231.                                  this, 
  232.                                  doc, 
  233.                                  browser);
  234.   doc.defaultView.addEventListener("pagehide", hideHandler, true);
  235.  
  236.   // More info than we technically need, but it comes in handy for debugging
  237.   var problem = {
  238.     "browser_": browser,
  239.     "doc_": doc,
  240.     "displayer_": displayer,
  241.     "url_": url,
  242.     "hideHandler_": hideHandler,
  243.   };
  244.   var numInQueue = this.queueProblem_(browser, problem);
  245.  
  246.   // If the queue was empty, schedule us to take something out
  247.   if (numInQueue == 1)
  248.     new G_Alarm(BindToObject(this.unqueueNextProblem_, this, browser), 0);
  249. }
  250.  
  251. /**
  252.  * Invoked when a problematic document is navigated away from. 
  253.  *
  254.  * @param doc Reference to the problematic Document navigated away from
  255.  
  256.  * @param browser Reference to the browser in which the problem document
  257.  *                unloaded
  258.  */
  259. PROT_BrowserView.prototype.onNavAwayFromProblem_ = function(doc, browser) {
  260.  
  261.   var problem = this.getProblem_(doc, browser);
  262.   // We want to know if the user navigated away from the phish site
  263.   // before or after viewing the warning.
  264.   var message = problem.displayer_.messageShowing_ ? "phishnavaway"
  265.                                                    : "ignorenavaway";
  266.   G_Debug(this, "User nav'd away from problem: " + message);
  267.   (new PROT_Reporter).report(message, problem.url_);
  268.  
  269.   G_Assert(this, doc === problem.doc_, "State doc not equal to nav away doc?");
  270.   G_Assert(this, browser === problem.browser_, 
  271.            "State browser not equal to nav away browser?");
  272.   
  273.   this.problemResolved(browser, doc);
  274. }
  275.  
  276. /**
  277.  * @param browser Reference to a browser we'd like to know about
  278.  * 
  279.  * @returns Boolean indicating if the browser in question has 
  280.  *          problematic content
  281.  */
  282. PROT_BrowserView.prototype.hasProblem = function(browser) {
  283.   return this.hasNonemptyProblemQueue_(browser);
  284. }
  285.  
  286. /**
  287.  * @param browser Reference to a browser we'd like to know about
  288.  *
  289.  * @returns Boolean indicating if the browser in question has a
  290.  *          problem (i.e., it has a non-empty problem queue)
  291.  */
  292. PROT_BrowserView.prototype.hasNonemptyProblemQueue_ = function(browser) {
  293.   try {
  294.     return !!browser.PROT_problemState__ && 
  295.       !!browser.PROT_problemState__.length;
  296.   } catch(e) {
  297.     // We could be checking a browser that has just been closed, in
  298.     // which case its properties will not be valid, causing the above
  299.     // statement to throw an error. Since this case handled elsewhere,
  300.     // just return false.
  301.     return false;
  302.   }
  303. }
  304.  
  305. /**
  306.  * Invoked to indicate that the problem for a particular problematic
  307.  * document in a browser has been resolved (e.g., by being navigated
  308.  * away from).
  309.  *
  310.  * @param browser Reference to the browser in which resolution is happening
  311.  *
  312.  * @param opt_doc Reference to the problematic doc whose problem was resolved
  313.  *                (if absent, assumes the doc assocaited with the currently
  314.  *                active displayer)
  315.  */
  316. PROT_BrowserView.prototype.problemResolved = function(browser, opt_doc) {
  317.   var problem;
  318.   var doc;
  319.   if (!!opt_doc) {
  320.     doc = opt_doc;
  321.     problem = this.getProblem_(doc, browser);
  322.   } else {
  323.     problem = this.getCurrentProblem_(browser);
  324.     doc = problem.doc_;
  325.   }
  326.  
  327.   problem.displayer_.done();
  328.   var wasHead = this.deleteProblemFromQueue_(doc, browser);
  329.  
  330.   // Peek at the next problem (if any) in the queue for this browser
  331.   var queueNotEmpty = this.getCurrentProblem_(browser);
  332.  
  333.   if (wasHead && queueNotEmpty) {
  334.     G_Debug(this, "More problems pending. Scheduling unqueue.");
  335.     new G_Alarm(BindToObject(this.unqueueNextProblem_, this, browser), 0);
  336.   }
  337. }
  338.  
  339. /**
  340.  * Peek at the top of the problem queue and if there's something there,
  341.  * make it active. 
  342.  *
  343.  * @param browser Reference to the browser we should activate a problem
  344.  *                displayer in if one is available
  345.  */
  346. PROT_BrowserView.prototype.unqueueNextProblem_ = function(browser) {
  347.   var problem = this.getCurrentProblem_(browser);
  348.   if (!problem) {
  349.     G_Debug(this, "No problem in queue; doc nav'd away from? (shrug)");
  350.     return;
  351.   }
  352.  
  353.   // Two problem docs that load in rapid succession could both schedule 
  354.   // themselves to be unqueued before this method is called. So ensure 
  355.   // that the problem at the head of the queue is not, in fact, active.
  356.   if (!problem.displayer_.isActive()) {
  357.  
  358.     // It could be the case that the server is really slow to respond,
  359.     // so there might not yet be anything in the problem Document. If
  360.     // we show the warning when that's the case, the user will see a
  361.     // blank document greyed out, and if they cancel the dialog
  362.     // they'll see the page they're navigating away from because it
  363.     // hasn't been painted over yet (b/c there's no content for the
  364.     // problem page). So here we ensure that we have content for the
  365.     // problem page before showing the dialog.
  366.     var haveContent = false;
  367.     try {
  368.       // This will throw if there's no content yet
  369.       var h = problem.doc_.defaultView.getComputedStyle(problem.doc_.body, "")
  370.               .getPropertyValue("height");
  371.       G_Debug(this, "body height: " + h);
  372.  
  373.       if (Number(h.substring(0, h.length - 2)))
  374.         haveContent = true;
  375.  
  376.     } catch (e) {
  377.       G_Debug(this, "Masked in unqueuenextproblem: " + e);
  378.     }
  379.     
  380.     if (!haveContent) {
  381.  
  382.       G_Debug(this, "Didn't get computed style. Re-queueing.");
  383.  
  384.       // One stuck problem document in a page shouldn't prevent us
  385.       // warning on other problem frames that might be loading or
  386.       // loaded. So stick the Document that doesn't have content
  387.       // back at the end of the queue.
  388.       var p = this.removeProblemFromQueue_(problem.doc_, browser);
  389.       G_Assert(this, p === problem, "Unqueued wrong problem?");
  390.       this.queueProblem_(browser, problem);
  391.  
  392.       // Try again in a bit. This opens us up to a potential
  393.       // vulnerability (put tons of hanging frames in a page
  394.       // ahead of your real phishy frame), but the risk at the
  395.       // moment is really low (plus it is outside our threat
  396.       // model).
  397.       new G_Alarm(BindToObject(this.unqueueNextProblem_, 
  398.                                this, 
  399.                                browser),
  400.                   200 /*ms*/);
  401.       return;
  402.     }
  403.  
  404.     problem.displayer_.start();
  405.  
  406.     // OK, we have content, but there there is an additional
  407.     // issue. Due to a bfcache bug, if we show the warning during
  408.     // paint suppression, the collapsing of the content pane affects
  409.     // the doc we're naving from :( The symptom is a page with grey
  410.     // screen on navigation to or from a phishing page (the
  411.     // contentDocument will have width zero).
  412.     //
  413.     // Paint supression lasts at most 250ms from when the parser sees
  414.     // the body, and the parser sees the body well before it has a
  415.     // height. We err on the side of caution.
  416.     //
  417.     // Thanks to bryner for helping to track the bfcache bug down.
  418.     // https://bugzilla.mozilla.org/show_bug.cgi?id=319646
  419.     
  420.     if (this.tabBrowser_.selectedBrowser === browser)
  421.       new G_Alarm(BindToObject(this.problemBrowserMaybeSelected, 
  422.                                this, 
  423.                                browser),
  424.                   350 /*ms*/);
  425.   }
  426. }
  427.  
  428. /**
  429.  * Helper function that adds a new problem to the queue of problems pending
  430.  * on this browser.
  431.  *
  432.  * @param browser Browser to which we should add state
  433.  *
  434.  * @param problem Object (structure, really) encapsulating the problem
  435.  *
  436.  * @returns Number indicating the number of items in the queue (and from
  437.  *          which you can infer whether the recently added item was
  438.  *          placed at the head, and hence should be active.
  439.  */
  440. PROT_BrowserView.prototype.queueProblem_ = function(browser, problem) {
  441.   G_Debug(this, "Adding problem state for " + problem.url_);
  442.  
  443.   if (this.hasNonemptyProblemQueue_(browser))
  444.     G_Debug(this, "Already has problem state. Queueing this problem...");
  445.  
  446.   // First problem ever signaled on this browser? Make a new queue!
  447.   if (browser.PROT_problemState__ == undefined)
  448.     browser.PROT_problemState__ = [];
  449.  
  450.   browser.PROT_problemState__.push(problem);
  451.   return browser.PROT_problemState__.length;
  452. }
  453.  
  454. /**
  455.  * Helper function that removes a problem from the queue and deactivates
  456.  * it.
  457.  *
  458.  * @param doc Reference to the doc for which we should remove state
  459.  *
  460.  * @param browser Reference to the browser from which we should remove
  461.  *                state
  462.  *
  463.  * @returns Boolean indicating if the remove problem was currently active
  464.  *          (that is, if it was at the head of the queue)
  465.  */
  466. PROT_BrowserView.prototype.deleteProblemFromQueue_ = function(doc, browser) {
  467.   G_Debug(this, "Deleting problem state for " + browser);
  468.   G_Assert(this, !!this.hasNonemptyProblemQueue_(browser),
  469.            "Browser has no problem state");
  470.  
  471.   var problem = this.getProblem_(doc, browser);
  472.   G_Assert(this, !!problem, "Couldnt find state in removeproblemstate???");
  473.  
  474.   var wasHead = browser.PROT_problemState__[0] === problem;
  475.   this.removeProblemFromQueue_(doc, browser);
  476.  
  477.   var hideHandler = problem.hideHandler_;
  478.   G_Assert(this, !!hideHandler, "No hidehandler in state?");
  479.   problem.doc_.defaultView.removeEventListener("pagehide",
  480.                                                hideHandler,
  481.                                                true);
  482.   return wasHead;
  483. }
  484.  
  485. /**
  486.  * Helper function that removes a problem from the queue but does 
  487.  * NOT deactivate it.
  488.  *
  489.  * @param doc Reference to the doc for which we should remove state
  490.  *
  491.  * @param browser Reference to the browser from which we should remove
  492.  *                state
  493.  *
  494.  * @returns Boolean indicating if the remove problem was currently active
  495.  *          (that is, if it was at the head of the queue)
  496.  */
  497. PROT_BrowserView.prototype.removeProblemFromQueue_ = function(doc, browser) {
  498.   G_Debug(this, "Removing problem state for " + browser);
  499.   G_Assert(this, !!this.hasNonemptyProblemQueue_(browser),
  500.            "Browser has no problem state");
  501.  
  502.   var problem = null;
  503.   // TODO Blech. Let's please have an abstraction here instead.
  504.   for (var i = 0; i < browser.PROT_problemState__.length; i++)
  505.     if (browser.PROT_problemState__[i].doc_ === doc) {
  506.       problem = browser.PROT_problemState__.splice(i, 1)[0];
  507.       break;
  508.     }
  509.   return problem;
  510. }
  511.  
  512. /**
  513.  * Retrieve (but do not remove) the problem state for a particular
  514.  * problematic Document in this browser
  515.  *
  516.  * @param doc Reference to the problematic doc to get state for
  517.  *
  518.  * @param browser Reference to the browser from which to get state
  519.  *
  520.  * @returns Object encapsulating the state we stored, or null if none
  521.  */
  522. PROT_BrowserView.prototype.getProblem_ = function(doc, browser) {
  523.   if (!this.hasNonemptyProblemQueue_(browser))
  524.     return null;
  525.  
  526.   // TODO Blech. Let's please have an abstraction here instead.
  527.   for (var i = 0; i < browser.PROT_problemState__.length; i++)
  528.     if (browser.PROT_problemState__[i].doc_ === doc)
  529.       return browser.PROT_problemState__[i];
  530.   return null;
  531. }
  532.  
  533. /**
  534.  * Retrieve the problem state for the currently active problem Document 
  535.  * in this browser
  536.  *
  537.  * @param browser Reference to the browser from which to get state
  538.  *
  539.  * @returns Object encapsulating the state we stored, or null if none
  540.  */
  541. PROT_BrowserView.prototype.getCurrentProblem_ = function(browser) {
  542.   return browser.PROT_problemState__[0];
  543. }
  544.  
  545. /**
  546.  * Invoked by the controller when the user switches tabs away from a problem 
  547.  * tab. 
  548.  *
  549.  * @param browser Reference to the tab that was switched from
  550.  */
  551. PROT_BrowserView.prototype.problemBrowserUnselected = function(browser) {
  552.   var problem = this.getCurrentProblem_(browser);
  553.   G_Assert(this, !!problem, "Couldn't get state from browser");
  554.   problem.displayer_.browserUnselected();
  555. }
  556.  
  557. /**
  558.  * Checks to see if the problem browser is selected, and if so, 
  559.  * tell it it to show its warning.
  560.  *
  561.  * @param browser Reference to the browser we wish to check
  562.  */
  563. PROT_BrowserView.prototype.problemBrowserMaybeSelected = function(browser) {
  564.   var problem = this.getCurrentProblem_(browser);
  565.  
  566.   if (this.tabBrowser_.selectedBrowser === browser &&
  567.       problem &&
  568.       problem.displayer_.isActive()) 
  569.     this.problemBrowserSelected(browser);
  570. }
  571.  
  572. /**
  573.  * Invoked by the controller when the user switches tabs to a problem tab
  574.  *
  575.  * @param browser Reference to the tab that was switched to
  576.  */
  577. PROT_BrowserView.prototype.problemBrowserSelected = function(browser) {
  578.   G_Debug(this, "Problem browser selected");
  579.   var problem = this.getCurrentProblem_(browser);
  580.   G_Assert(this, !!problem, "No state? But we're selected!");
  581.   problem.displayer_.browserSelected();
  582. }
  583.  
  584. /**
  585.  * Invoked by the controller when the user accepts our warning. Passes
  586.  * the accept through to the message displayer, which knows what to do
  587.  * (it will be displayer-specific).
  588.  *
  589.  * @param browser Reference to the browser for which the user accepted
  590.  *                our warning
  591.  */
  592. PROT_BrowserView.prototype.acceptAction = function(browser) {
  593.   var problem = this.getCurrentProblem_(browser);
  594.  
  595.   // We run the action only after we're completely through processing
  596.   // this event. We do this because the action could cause state to be
  597.   // cleared (e.g., by navigating the problem document) that we need
  598.   // to finish processing the event.
  599.  
  600.   new G_Alarm(BindToObject(problem.displayer_.acceptAction, 
  601.                            problem.displayer_), 
  602.               0);
  603. }
  604.  
  605. /**
  606.  * Invoked by the controller when the user declines our
  607.  * warning. Passes the decline through to the message displayer, which
  608.  * knows what to do (it will be displayer-specific).
  609.  *
  610.  * @param browser Reference to the browser for which the user declined
  611.  *                our warning
  612.  */
  613. PROT_BrowserView.prototype.declineAction = function(browser) {
  614.   var problem = this.getCurrentProblem_(browser);
  615.   G_Assert(this, !!problem, "User declined but no state???");
  616.  
  617.   // We run the action only after we're completely through processing
  618.   // this event. We do this because the action could cause state to be
  619.   // cleared (e.g., by navigating the problem document) that we need
  620.   // to finish processing the event.
  621.  
  622.   new G_Alarm(BindToObject(problem.displayer_.declineAction, 
  623.                            problem.displayer_), 
  624.               0);
  625. }
  626.  
  627. /**
  628.  * The user wants to see the warning message. So let em! At some point when
  629.  * we have multiple types of warnings, we'll have to mediate them here.
  630.  *
  631.  * @param browser Reference to the browser that has the warning the user 
  632.  *                wants to see. 
  633.  */
  634. PROT_BrowserView.prototype.explicitShow = function(browser) {
  635.   var problem = this.getCurrentProblem_(browser);
  636.   G_Assert(this, !!problem, "Explicit show on browser w/o problem state???");
  637.   problem.displayer_.explicitShow();
  638. }
  639. //@line 37 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\controller.js"
  640.  
  641.  
  642. // This is our controller -- the thingy that listens to what the user
  643. // is doing. There is one controller per browser window, and each has
  644. // a BrowserView that manages information about problems within the
  645. // window. The controller figures out when the browser might want to
  646. // know about something, but the browser view figures out what exactly
  647. // to do (and the BrowserView's displayer figures out how to do it).
  648. //
  649. // For example, the controller might notice that the user has switched
  650. // to a tab that has something problematic in it. It would tell its 
  651. // BrowserView this, and the BrowserView would figure out whether it 
  652. // is appropriate to show a warning (e.g., perhaps the user previously
  653. // dismissed the warning for that problem). If so, the BrowserView tells
  654. // the displayer to show the warning. Anyhoo...
  655. //
  656. // TODO Could move all browser-related hide/show logic into the browser
  657. //      view. Need to think about this more.
  658.  
  659. /**
  660.  * Handles user actions, translating them into messages to the view
  661.  *
  662.  * @constructor
  663.  * @param win Reference to the Window (browser window context) we should
  664.  *            attach to
  665.  * @param tabBrowser  Reference to the window's main tabbrowser object.
  666.  * @param phishingWarden Reference to the PhishingWarden we should register
  667.  *                       our browserview with
  668.  */
  669. function PROT_Controller(win, tabBrowser, phishingWarden) {
  670.   this.debugZone = "controller";
  671.  
  672.   this.win_ = win;
  673.   this.phishingWarden_ = phishingWarden;
  674.  
  675.   // Use this to query preferences
  676.   this.prefs_ = new G_Preferences();
  677.  
  678.   // Set us up to receive the events we want.
  679.   this.tabBrowser_ = tabBrowser;
  680.   this.onTabSwitchClosure_ = BindToObject(this.onTabSwitch, this);
  681.   this.tabBrowser_.mTabBox.addEventListener("select", this.onTabSwitchClosure_, true);
  682.  
  683.   // Used to determine when the user has switched tabs
  684.   this.lastTab_ = tabBrowser.selectedBrowser;
  685.  
  686.   // Install our command controllers. These commands are issued from
  687.   // various places in our UI, including our preferences dialog, the
  688.   // warning dialog, etc.
  689.   var commandHandlers = { 
  690.     "safebrowsing-show-warning" :
  691.       BindToObject(this.onUserShowWarning, this),
  692.     "safebrowsing-accept-warning" :
  693.       BindToObject(this.onUserAcceptWarning, this),
  694.     "safebrowsing-decline-warning" :
  695.       BindToObject(this.onUserDeclineWarning, this),
  696.   };
  697.  
  698.   this.commandController_ = new PROT_CommandController(commandHandlers);
  699.   this.win_.controllers.appendController(this.commandController_);
  700.  
  701.   // This guy embodies the logic of when to display warnings
  702.   // (displayers embody the how).
  703.   this.browserView_ = new PROT_BrowserView(this.tabBrowser_);
  704.  
  705.   // We need to let the phishing warden know about this browser view so it 
  706.   // can be given the opportunity to handle problem documents. We also need
  707.   // to let the warden know when this window and hence this browser view
  708.   // is going away.
  709.   this.phishingWarden_.addBrowserView(this.browserView_);
  710.  
  711.   G_Debug(this, "Controller initialized.");
  712. }
  713.  
  714. /**
  715.  * Invoked when the browser window is closing. Do some cleanup.
  716.  */
  717. PROT_Controller.prototype.shutdown = function(e) {
  718.   G_Debug(this, "Browser window closing. Shutting controller down.");
  719.   if (this.browserView_) {
  720.     this.phishingWarden_.removeBrowserView(this.browserView_);
  721.   }
  722.  
  723.   if (this.commandController_) {
  724.     this.win_.controllers.removeController(this.commandController_);
  725.     this.commandController_ = null;
  726.   }
  727.  
  728.   // No need to drain the browser view's problem queue explicitly; it will
  729.   // receive pagehides for all the browsers in its queues as they're torn
  730.   // down, and it will remove them.
  731.   this.browserView_ = null;
  732.  
  733.   if (this.tabBrowser_)
  734.     this.tabBrowser_.mTabBox.removeEventListener("select", this.onTabSwitchClosure_, true);
  735.   // Break circular refs so we can be gc'ed.
  736.   this.tabBrowser_ = this.lastTab_ = null;
  737.  
  738.   this.win_.removeEventListener("unload", this.onShutdown_, false);
  739.   this.prefs_ = null;
  740.  
  741.   G_Debug(this, "Controller shut down.");
  742. }
  743.  
  744. /**
  745.  * The user clicked the urlbar icon; they want to see the warning message
  746.  * again.
  747.  */
  748. PROT_Controller.prototype.onUserShowWarning = function() {
  749.   var browser = this.tabBrowser_.selectedBrowser;
  750.   this.browserView_.explicitShow(browser);
  751. }
  752.  
  753. /**
  754.  * Deal with a user accepting our warning. 
  755.  *
  756.  * TODO the warning hide/display instructions here can probably be moved
  757.  * into the browserview in the future, given its knowledge of when the
  758.  * problem doc hides/shows.
  759.  */
  760. PROT_Controller.prototype.onUserAcceptWarning = function() {
  761.   G_Debug(this, "User accepted warning.");
  762.   var browser = this.tabBrowser_.selectedBrowser;
  763.   G_Assert(this, !!browser, "Couldn't get current browser?!?");
  764.   G_Assert(this, this.browserView_.hasProblem(browser),
  765.            "User accept fired, but browser doesn't have warning showing?!?");
  766.  
  767.   this.browserView_.acceptAction(browser);
  768.   this.browserView_.problemResolved(browser);
  769. }
  770.  
  771. /**
  772.  * Deal with a user declining our warning. 
  773.  *
  774.  * TODO the warning hide/display instructions here can probably be moved
  775.  * into the browserview in the future, given its knowledge of when the
  776.  * problem doc hides/shows.
  777.  */
  778. PROT_Controller.prototype.onUserDeclineWarning = function() {
  779.   G_Debug(this, "User declined warning.");
  780.   var browser = this.tabBrowser_.selectedBrowser;
  781.   G_Assert(this, this.browserView_.hasProblem(browser),
  782.            "User decline fired, but browser doesn't have warning showing?!?");
  783.   this.browserView_.declineAction(browser);
  784.   // We don't call problemResolved() here because all declining does it
  785.   // hide the message; we still have the urlbar icon showing, giving
  786.   // the user the ability to bring the warning message back up if they
  787.   // so desire.
  788. }
  789.  
  790. /**
  791.  * Notice tab switches, and display or hide warnings as appropriate.
  792.  *
  793.  * TODO this logic can probably move into the browser view at some
  794.  * point. But one thing at a time.
  795.  */
  796. PROT_Controller.prototype.onTabSwitch = function(e) {
  797.   // Filter spurious events
  798.   // The event target is usually tabs but can be tabpanels when tabs were opened
  799.   // programatically via tabbrowser.addTab().
  800.   if (!e.target || (e.target.localName != "tabs" && e.target.localName != "tabpanels"))
  801.     return;
  802.  
  803.   var fromBrowser = this.lastTab_;
  804.   var toBrowser = this.tabBrowser_.selectedBrowser;
  805.  
  806.   if (fromBrowser != toBrowser) {
  807.     this.lastTab_ = toBrowser;
  808.  
  809.     if (this.browserView_.hasProblem(fromBrowser)) 
  810.       this.browserView_.problemBrowserUnselected(fromBrowser);
  811.  
  812.     if (this.browserView_.hasProblem(toBrowser))
  813.       this.browserView_.problemBrowserSelected(toBrowser);
  814.   }
  815. }
  816. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\firefox-commands.js"
  817.  
  818.  
  819. // Some misc command-related plumbing used by the controller.
  820.  
  821.  
  822. /**
  823.  * A tiny wrapper class for super-simple command handlers.
  824.  *
  825.  * @param commandHandlerMap An object containing name/value pairs where
  826.  *                          the name is command name (string) and value
  827.  *                          is the function to execute for that command
  828.  */
  829. function PROT_CommandController(commandHandlerMap) {
  830.   this.debugZone = "commandhandler";
  831.   this.cmds_ = commandHandlerMap;
  832. }
  833.  
  834. /**
  835.  * @param cmd Command to query support for
  836.  * @returns Boolean indicating if this controller supports cmd
  837.  */
  838. PROT_CommandController.prototype.supportsCommand = function(cmd) { 
  839.   return (cmd in this.cmds_); 
  840. }
  841.  
  842. /**
  843.  * Trivial implementation
  844.  *
  845.  * @param cmd Command to query status of
  846.  */
  847. PROT_CommandController.prototype.isCommandEnabled = function(cmd) { 
  848.   return true; 
  849. }
  850.   
  851. /**
  852.  * Execute a command
  853.  *
  854.  * @param cmd Command to execute
  855.  */
  856. PROT_CommandController.prototype.doCommand = function(cmd) {
  857.   return this.cmds_[cmd](); 
  858. }
  859.  
  860. /**
  861.  * Trivial implementation
  862.  */
  863. PROT_CommandController.prototype.onEvent = function(cmd) { }
  864.  
  865. //@line 37 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\globalstore.js"
  866.  
  867.  
  868. // A class that encapsulates data provider specific values.  The
  869. // root of the provider pref tree is browser.safebrowsing.provider.
  870. // followed by a number, followed by specific properties.  The properties
  871. // that a data provider can supply are:
  872. //
  873. // name: The name of the provider
  874. // lookupURL: The URL to send requests to in enhanced mode
  875. // keyURL: Before we send URLs in enhanced mode, we need to encrypt them
  876. // reportURL: When shown a warning bubble, we send back the user decision
  877. //            (get me out of here/ignore warning) to this URL (strip cookies
  878. //            first).  This is optional.
  879. // reportGenericURL: HTML page for general user feedback
  880. // reportPhishURL: HTML page for notifying the provider of a new phishing page
  881. // reportErrorURL: HTML page for notifying the provider of a false positive
  882.  
  883. const kDataProviderIdPref = 'browser.safebrowsing.dataProvider';
  884. const kProviderBasePref = 'browser.safebrowsing.provider.';
  885.  
  886. //@line 58 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\globalstore.js"
  887. const MOZ_OFFICIAL_BUILD = true;
  888. //@line 62 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\globalstore.js"
  889.  
  890. const MOZ_PARAM_LOCALE = /\{moz:locale\}/g;
  891. const MOZ_PARAM_CLIENT = /\{moz:client\}/g;
  892. const MOZ_PARAM_BUILDID = /\{moz:buildid\}/g;
  893. const MOZ_PARAM_VERSION = /\{moz:version\}/g;
  894.  
  895. /**
  896.  * Information regarding the data provider.
  897.  */
  898. function PROT_DataProvider() {
  899.   this.prefs_ = new G_Preferences();
  900.  
  901.   this.loadDataProviderPrefs_();
  902.   
  903.   // Watch for changes in the data provider and update accordingly.
  904.   this.prefs_.addObserver(kDataProviderIdPref,
  905.                           BindToObject(this.loadDataProviderPrefs_, this));
  906.  
  907.   // Watch for when anti-phishing is toggled on or off.
  908.   this.prefs_.addObserver(kPhishWardenEnabledPref,
  909.                           BindToObject(this.loadDataProviderPrefs_, this));
  910.  
  911.   // Watch for when remote lookups are toggled on or off.
  912.   this.prefs_.addObserver(kPhishWardenRemoteLookups,
  913.                           BindToObject(this.loadDataProviderPrefs_, this));
  914. }
  915.  
  916. /**
  917.  * Populate all the provider variables.  We also call this when whenever
  918.  * the provider id changes.
  919.  */
  920. PROT_DataProvider.prototype.loadDataProviderPrefs_ = function() {
  921.   // Currently, there's no UI for changing local list provider so we
  922.   // hard code the value for provider 0.
  923.   this.updateURL_ = this.getUrlPref_(
  924.         'browser.safebrowsing.provider.0.updateURL');
  925.  
  926.   var id = this.prefs_.getPref(kDataProviderIdPref, null);
  927.  
  928.   // default to 0
  929.   if (null == id)
  930.     id = 0;
  931.   
  932.   var basePref = kProviderBasePref + id + '.';
  933.  
  934.   this.name_ = this.prefs_.getPref(basePref + "name", "");
  935.  
  936.   // Urls used to get data from a provider
  937.   this.lookupURL_ = this.getUrlPref_(basePref + "lookupURL");
  938.   this.keyURL_ = this.getUrlPref_(basePref + "keyURL");
  939.   this.reportURL_ = this.getUrlPref_(basePref + "reportURL");
  940.  
  941.   // Urls to HTML report pages
  942.   this.reportGenericURL_ = this.getUrlPref_(basePref + "reportGenericURL");
  943.   this.reportErrorURL_ = this.getUrlPref_(basePref + "reportErrorURL");
  944.   this.reportPhishURL_ = this.getUrlPref_(basePref + "reportPhishURL");
  945.  
  946.   // Propagate the changes to the list-manager.
  947.   this.updateListManager_();
  948. }
  949.  
  950. /**
  951.  * The list manager needs urls to operate.  It needs a url to know where the
  952.  * table updates are, and it needs a url for decrypting enchash style tables.
  953.  */
  954. PROT_DataProvider.prototype.updateListManager_ = function() {
  955.   var listManager = Cc["@mozilla.org/url-classifier/listmanager;1"]
  956.                       .getService(Ci.nsIUrlListManager);
  957.  
  958.   // If we add support for changing local data providers, we need to add a
  959.   // pref observer that sets the update url accordingly.
  960.   listManager.setUpdateUrl(this.getUpdateURL());
  961.  
  962.   // setKeyUrl has the side effect of fetching a key from the server.
  963.   // This shouldn't happen if anti-phishing is disabled or we're in local
  964.   // list mode, so we need to check for that.
  965.   var isEnabled = this.prefs_.getPref(kPhishWardenEnabledPref, false);
  966.   var remoteLookups = this.prefs_.getPref(kPhishWardenRemoteLookups, false);
  967.   if (isEnabled && remoteLookups) {
  968.     listManager.setKeyUrl(this.getKeyURL());
  969.   } else {
  970.     // Clear the key to stop updates.
  971.     listManager.setKeyUrl("");
  972.   }
  973. }
  974.  
  975. /**
  976.  * Lookup the value of a URL from prefs file and do parameter substitution.
  977.  */
  978. PROT_DataProvider.prototype.getUrlPref_ = function(prefName) {
  979.   var url = this.prefs_.getPref(prefName);
  980.  
  981.   var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
  982.                           .getService(Components.interfaces.nsIXULAppInfo);
  983.  
  984.   var mozClientStr = MOZ_OFFICIAL_BUILD ? 'navclient-auto-ffox' : appInfo.name;
  985.  
  986.   // Parameter substitution
  987.   url = url.replace(MOZ_PARAM_LOCALE, this.getLocale_());
  988.   url = url.replace(MOZ_PARAM_CLIENT, mozClientStr);
  989.   url = url.replace(MOZ_PARAM_BUILDID, appInfo.appBuildID);
  990.   url = url.replace(MOZ_PARAM_VERSION, appInfo.version);
  991.   return url;
  992. }
  993.  
  994. /**
  995.  * @return String the browser locale (similar code is in nsSearchService.js)
  996.  */
  997. PROT_DataProvider.prototype.getLocale_ = function() {
  998.   const localePref = "general.useragent.locale";
  999.   var locale = this.getLocalizedPref_(localePref);
  1000.   if (locale)
  1001.     return locale;
  1002.  
  1003.   // Not localized
  1004.   var prefs = new G_Preferences();
  1005.   return prefs.getPref(localePref, "");
  1006. }
  1007.  
  1008. /**
  1009.  * @return String name of the localized pref, null if none exists.
  1010.  */
  1011. PROT_DataProvider.prototype.getLocalizedPref_ = function(aPrefName) {
  1012.   // G_Preferences doesn't know about complex values, so we use the
  1013.   // xpcom object directly.
  1014.   var prefs = Cc["@mozilla.org/preferences-service;1"]
  1015.               .getService(Ci.nsIPrefBranch);
  1016.   try {
  1017.     return prefs.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data;
  1018.   } catch (ex) {
  1019.   }
  1020.   return "";
  1021. }
  1022.  
  1023. //////////////////////////////////////////////////////////////////////////////
  1024. // Getters for the remote provider pref values mentioned above.
  1025. PROT_DataProvider.prototype.getName = function() {
  1026.   return this.name_;
  1027. }
  1028.  
  1029. PROT_DataProvider.prototype.getUpdateURL = function() {
  1030.   return this.updateURL_;
  1031. }
  1032.  
  1033. PROT_DataProvider.prototype.getLookupURL = function() {
  1034.   return this.lookupURL_;
  1035. }
  1036. PROT_DataProvider.prototype.getKeyURL = function() {
  1037.   return this.keyURL_;
  1038. }
  1039. PROT_DataProvider.prototype.getReportURL = function() {
  1040.   return this.reportURL_;
  1041. }
  1042.  
  1043. PROT_DataProvider.prototype.getReportGenericURL = function() {
  1044.   return this.reportGenericURL_;
  1045. }
  1046. PROT_DataProvider.prototype.getReportErrorURL = function() {
  1047.   return this.reportErrorURL_;
  1048. }
  1049. PROT_DataProvider.prototype.getReportPhishURL = function() {
  1050.   return this.reportPhishURL_;
  1051. }
  1052. //@line 22 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\list-warden.js"
  1053.  
  1054. //@line 38 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\list-warden.js"
  1055.  
  1056. // A warden that knows how to register lists with a listmanager and keep them
  1057. // updated if necessary.  The ListWarden also provides a simple interface to
  1058. // check if a URL is evil or not.  Specialized wardens like the PhishingWarden
  1059. // inherit from it.
  1060. //
  1061. // Classes that inherit from ListWarden are responsible for calling
  1062. // enableTableUpdates or disableTableUpdates.  This usually entails
  1063. // registering prefObservers and calling enable or disable in the base
  1064. // class as appropriate.
  1065. //
  1066.  
  1067. /**
  1068.  * Abtracts the checking of user/browser actions for signs of
  1069.  * phishing. 
  1070.  *
  1071.  * @constructor
  1072.  */
  1073. function PROT_ListWarden() {
  1074.   this.debugZone = "listwarden";
  1075.   var listManager = Cc["@mozilla.org/url-classifier/listmanager;1"]
  1076.                       .getService(Ci.nsIUrlListManager);
  1077.   this.listManager_ = listManager;
  1078.  
  1079.   // Once we register tables, their respective names will be listed here.
  1080.   this.blackTables_ = [];
  1081.   this.whiteTables_ = [];
  1082. }
  1083.  
  1084. PROT_ListWarden.IN_BLACKLIST = 0
  1085. PROT_ListWarden.IN_WHITELIST = 1
  1086. PROT_ListWarden.NOT_FOUND = 2
  1087.  
  1088. /**
  1089.  * Tell the ListManger to keep all of our tables updated
  1090.  */
  1091.  
  1092. PROT_ListWarden.prototype.enableBlacklistTableUpdates = function() {
  1093.   for (var i = 0; i < this.blackTables_.length; ++i) {
  1094.     this.listManager_.enableUpdate(this.blackTables_[i]);
  1095.   }
  1096. }
  1097.  
  1098. /**
  1099.  * Tell the ListManager to stop updating our tables
  1100.  */
  1101.  
  1102. PROT_ListWarden.prototype.disableBlacklistTableUpdates = function() {
  1103.   for (var i = 0; i < this.blackTables_.length; ++i) {
  1104.     this.listManager_.disableUpdate(this.blackTables_[i]);
  1105.   }
  1106. }
  1107.  
  1108. /**
  1109.  * Tell the ListManager to update whitelist tables.  They may be enabled even
  1110.  * when other updates aren't, for performance reasons.
  1111.  */
  1112. PROT_ListWarden.prototype.enableWhitelistTableUpdates = function() {
  1113.   for (var i = 0; i < this.whiteTables_.length; ++i) {
  1114.     this.listManager_.enableUpdate(this.whiteTables_[i]);
  1115.   }
  1116. }
  1117.  
  1118. /**
  1119.  * Tell the ListManager to stop updating whitelist tables.
  1120.  */
  1121. PROT_ListWarden.prototype.disableWhitelistTableUpdates = function() {
  1122.   for (var i = 0; i < this.whiteTables_.length; ++i) {
  1123.     this.listManager_.disableUpdate(this.whiteTables_[i]);
  1124.   }
  1125. }
  1126.  
  1127. /**
  1128.  * Register a new black list table with the list manager
  1129.  * @param tableName - name of the table to register
  1130.  * @returns true if the table could be registered, false otherwise
  1131.  */
  1132.  
  1133. PROT_ListWarden.prototype.registerBlackTable = function(tableName) {
  1134.   var result = this.listManager_.registerTable(tableName, false);
  1135.   if (result) {
  1136.     this.blackTables_.push(tableName);
  1137.   }
  1138.   return result;
  1139. }
  1140.  
  1141. /**
  1142.  * Register a new white list table with the list manager
  1143.  * @param tableName - name of the table to register
  1144.  * @returns true if the table could be registered, false otherwise
  1145.  */
  1146.  
  1147. PROT_ListWarden.prototype.registerWhiteTable = function(tableName) {
  1148.   var result = this.listManager_.registerTable(tableName, false);
  1149.   if (result) {
  1150.     this.whiteTables_.push(tableName);
  1151.   }
  1152.   return result;
  1153. }
  1154.  
  1155. /**
  1156.  * Method that looks up a url on the whitelist.
  1157.  *
  1158.  * @param url The URL to check
  1159.  * @param callback Function with a single param:
  1160.  *       PROT_ListWarden.IN_BLACKLIST, PROT_ListWarden.IN_WHITELIST,
  1161.  *       or PROT_ListWarden.NOT_FOUND
  1162.  */
  1163. PROT_ListWarden.prototype.isWhiteURL = function(url, callback) {
  1164.   (new MultiTableQuerier(url,
  1165.                          this.whiteTables_,
  1166.                          [] /* no blacklists */,
  1167.                          callback)).run();
  1168. }
  1169.  
  1170. /**
  1171.  * Method that looks up a url in both the white and black lists.
  1172.  *
  1173.  * If there is conflict, the white list has precedence over the black list.
  1174.  *
  1175.  * This is tricky because all database queries are asynchronous.  So we need
  1176.  * to chain together our checks against white and black tables.  We use
  1177.  * MultiTableQuerier (see below) to manage this.
  1178.  *
  1179.  * @param url URL to look up
  1180.  * @param callback Function with a single param:
  1181.  *       PROT_ListWarden.IN_BLACKLIST, PROT_ListWarden.IN_WHITELIST,
  1182.  *       or PROT_ListWarden.NOT_FOUND
  1183.  */
  1184. PROT_ListWarden.prototype.isEvilURL = function(url, callback) {
  1185.   (new MultiTableQuerier(url,
  1186.                          this.whiteTables_,
  1187.                          this.blackTables_,
  1188.                          callback)).run();
  1189. }
  1190.  
  1191. /**
  1192.  * This class helps us query multiple tables even though each table check
  1193.  * is asynchronous.  It provides callbacks for each listManager lookup
  1194.  * and decides whether we need to continue querying or not.  After
  1195.  * instantiating the method, use run() to invoke.
  1196.  *
  1197.  * @param url String The url to check
  1198.  * @param whiteTables Array of strings with each white table name
  1199.  * @param blackTables Array of strings with each black table name
  1200.  * @param callback Function to call with result 
  1201.  *       PROT_ListWarden.IN_BLACKLIST, PROT_ListWarden.IN_WHITELIST,
  1202.  *       or PROT_ListWarden.NOT_FOUND
  1203.  */
  1204. function MultiTableQuerier(url, whiteTables, blackTables, callback) {
  1205.   this.debugZone = "multitablequerier";
  1206.   this.url_ = url;
  1207.  
  1208.   this.whiteTables_ = {};
  1209.   for (var i = 0; i < whiteTables.length; i++) {
  1210.     this.whiteTables_[whiteTables[i]] = true;
  1211.   }
  1212.  
  1213.   this.blackTables_ = {};
  1214.   for (var i = 0; i < blackTables.length; i++) {
  1215.     this.blackTables_[blackTables[i]] = true;
  1216.   }
  1217.  
  1218.   this.callback_ = callback;
  1219.   this.listManager_ = Cc["@mozilla.org/url-classifier/listmanager;1"]
  1220.                       .getService(Ci.nsIUrlListManager);
  1221. }
  1222.  
  1223. MultiTableQuerier.prototype.run = function() {
  1224.   /* ask the dbservice for all the tables to which this URL belongs */
  1225.   this.listManager_.safeLookup(this.url_,
  1226.                                BindToObject(this.lookupCallback_, this));
  1227. }
  1228.  
  1229. MultiTableQuerier.prototype.lookupCallback_ = function(result) {
  1230.   if (result == "") {
  1231.     this.callback_(PROT_ListWarden.NOT_FOUND);
  1232.     return;
  1233.   }
  1234.  
  1235.   var tableNames = result.split(",");
  1236.  
  1237.   /* Check the whitelists */
  1238.   for (var i = 0; i < tableNames.length; i++) {
  1239.     if (tableNames[i] && this.whiteTables_[tableNames[i]]) {
  1240.       this.callback_(PROT_ListWarden.IN_WHITELIST);
  1241.       return;
  1242.     }
  1243.   }
  1244.  
  1245.   /* Check the blacklists */
  1246.   for (var i = 0; i < tableNames.length; i++) {
  1247.     if (tableNames[i] && this.blackTables_[tableNames[i]]) {
  1248.       this.callback_(PROT_ListWarden.IN_BLACKLIST);
  1249.       return;
  1250.     }
  1251.   }
  1252.  
  1253.   /* Not in any lists we know about */
  1254.   this.callback_(PROT_ListWarden.NOT_FOUND);
  1255. }
  1256. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\phishing-afterload-displayer.js"
  1257.  
  1258.  
  1259. // Implementation of the warning message we show users when we
  1260. // notice navigation to a phishing page after it has loaded. The
  1261. // browser view encapsulates all the hide/show logic, so the displayer
  1262. // doesn't need to know when to display itself, only how.
  1263. //
  1264. // Displayers implement the following interface:
  1265. //
  1266. // start() -- fired to initialize the displayer (to make it active). When
  1267. //            called, this displayer starts listening for and responding to
  1268. //            events. At most one displayer per tab should be active at a 
  1269. //            time, and start() should be called at most once.
  1270. // declineAction() -- fired when the user declines the warning.
  1271. // acceptAction() -- fired when the user declines the warning
  1272. // explicitShow() -- fired when the user wants to see the warning again
  1273. // browserSelected() -- the browser is the top tab
  1274. // browserUnselected() -- the browser is no long the top tab
  1275. // done() -- clean up. May be called once (even if the displayer wasn't
  1276. //           activated).
  1277. //
  1278. // At the moment, all displayers share access to the same xul in 
  1279. // safebrowsing-overlay.xul. Hence the need for at most one displayer
  1280. // to be active per tab at a time.
  1281.  
  1282. /**
  1283.  * Factory that knows how to create a displayer appropriate to the
  1284.  * user's platform. We use a clunky canvas-based displayer for all
  1285.  * platforms until such time as we can overlay semi-transparent
  1286.  * areas over browser content.
  1287.  *
  1288.  * See the base object for a description of the constructor args
  1289.  *
  1290.  * @constructor
  1291.  */
  1292. function PROT_PhishMsgDisplayer(msgDesc, browser, doc, url) {
  1293.  
  1294.   // TODO: Change this to return a PhishMsgDisplayerTransp on windows
  1295.   // (and maybe other platforms) when Firefox 2.0 hits.
  1296.  
  1297.   return new PROT_PhishMsgDisplayerCanvas(msgDesc, browser, doc, url);
  1298. }
  1299.  
  1300.  
  1301. /**
  1302.  * Base class that implements most of the plumbing required to hide
  1303.  * and show a phishing warning. Subclasses implement the actual
  1304.  * showMessage and hideMessage methods. 
  1305.  *
  1306.  * This class is not meant to be instantiated directly.
  1307.  *
  1308.  * @param msgDesc String describing the kind of warning this is
  1309.  * @param browser Reference to the browser over which we display the msg
  1310.  * @param doc Reference to the document in which browser is found
  1311.  * @param url String containing url of the problem document
  1312.  * @constructor
  1313.  */
  1314. function PROT_PhishMsgDisplayerBase(msgDesc, browser, doc, url) {
  1315.   this.debugZone = "phishdisplayer";
  1316.   this.msgDesc_ = msgDesc;                                // currently unused
  1317.   this.browser_ = browser;
  1318.   this.doc_ = doc;
  1319.   this.url_ = url;
  1320.  
  1321.   // We'll need to manipulate the XUL in safebrowsing-overlay.xul
  1322.   this.messageId_ = "safebrowsing-palm-message";
  1323.   this.messageTailId_ = "safebrowsing-palm-message-tail-container";
  1324.   this.messageContentId_ = "safebrowsing-palm-message-content";
  1325.   this.extendedMessageId_ = "safebrowsing-palm-extended-message";
  1326.   this.showmoreLinkId_ = "safebrowsing-palm-showmore-link";
  1327.   this.faqLinkId_ = "safebrowsing-palm-faq-link";
  1328.   this.urlbarIconId_ = "safebrowsing-urlbar-icon";
  1329.   this.refElementId_ = this.urlbarIconId_;
  1330.  
  1331.   // We use this to report user actions to the server
  1332.   this.reporter_ = new PROT_Reporter();
  1333.  
  1334.   // The active UI elements in our warning send these commands; bind them
  1335.   // to their handlers but don't register the commands until we start
  1336.   // (because another displayer might be active)
  1337.   this.commandHandlers_ = {
  1338.     "safebrowsing-palm-showmore":
  1339.       BindToObject(this.showMore_, this),
  1340.   };
  1341.  
  1342.   this.windowWatcher_ = 
  1343.     Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  1344.     .getService(Components.interfaces.nsIWindowWatcher);
  1345. }
  1346.  
  1347. /**
  1348.  * @returns The default background color of the browser
  1349.  */
  1350. PROT_PhishMsgDisplayerBase.prototype.getBackgroundColor_ = function() {
  1351.   var pref = Components.classes["@mozilla.org/preferences-service;1"].
  1352.              getService(Components.interfaces.nsIPrefBranch);
  1353.   return pref.getCharPref("browser.display.background_color");
  1354. }
  1355.  
  1356. /**
  1357.  * Fired when the user declines our warning. Report it!
  1358.  */
  1359. PROT_PhishMsgDisplayerBase.prototype.declineAction = function() {
  1360.   G_Debug(this, "User declined warning.");
  1361.   G_Assert(this, this.started_, "Decline on a non-active displayer?");
  1362.   this.reporter_.report("phishdecline", this.url_);
  1363.  
  1364.   this.messageShouldShow_ = false;
  1365.   if (this.messageShowing_)
  1366.     this.hideMessage_();
  1367. }
  1368.  
  1369. /**
  1370.  * Fired when the user accepts our warning
  1371.  */
  1372. PROT_PhishMsgDisplayerBase.prototype.acceptAction = function() {
  1373.   G_Assert(this, this.started_, "Accept on an unstarted displayer?");
  1374.   G_Assert(this, this.done_, "Accept on a finished displayer?");
  1375.   G_Debug(this, "User accepted warning.");
  1376.   this.reporter_.report("phishaccept", this.url_);
  1377.  
  1378.   var url = this.getMeOutOfHereUrl_();
  1379.   this.browser_.loadURI(url);
  1380. }
  1381.  
  1382. /**
  1383.  * Get the url for "Get me out of here."  This is the browser's default home
  1384.  * page, or, about:blank.
  1385.  * @return String url
  1386.  */
  1387. PROT_PhishMsgDisplayerBase.prototype.getMeOutOfHereUrl_ = function() {
  1388.   // Try to get their homepage from prefs.
  1389.   var prefs = Cc["@mozilla.org/preferences-service;1"]
  1390.               .getService(Ci.nsIPrefService).getDefaultBranch(null);
  1391.  
  1392.   var url = "about:blank";
  1393.   try {
  1394.     url = prefs.getComplexValue("browser.startup.homepage",
  1395.                                 Ci.nsIPrefLocalizedString).data;
  1396.     // If url is a pipe-delimited set of pages, just take the first one.
  1397.     // This will need to change once bug 221445 is fixed.
  1398.     if (url.indexOf("|") != -1)
  1399.       url = url.split("|")[0];
  1400.   } catch(e) {
  1401.     G_Debug(this, "Couldn't get homepage pref: " + e);
  1402.   }
  1403.   
  1404.   return url;
  1405. }
  1406.  
  1407. /**
  1408.  * Invoked when the browser is resized
  1409.  */
  1410. PROT_PhishMsgDisplayerBase.prototype.onBrowserResized_ = function(event) {
  1411.   G_Debug(this, "Got resize for " + event.target);
  1412.  
  1413.   if (this.messageShowing_) {
  1414.     this.hideMessage_(); 
  1415.     this.showMessage_();
  1416.   }
  1417. }
  1418.  
  1419. /**
  1420.  * Invoked by the browser view when our browser is switched to
  1421.  */
  1422. PROT_PhishMsgDisplayerBase.prototype.browserSelected = function() {
  1423.   G_Assert(this, this.started_, "Displayer selected before being started???");
  1424.  
  1425.   // If messageshowing hasn't been set, then this is the first time this
  1426.   // problematic browser tab has been on top, so do our setup and show
  1427.   // the warning.
  1428.   if (this.messageShowing_ === undefined) {
  1429.     this.messageShouldShow_ = true;
  1430.   }
  1431.  
  1432.   this.addWarningInUrlbar_();  // Goes away when we are unselected or unloaded
  1433.  
  1434.   // messageShouldShow might be false if the user dismissed the warning, 
  1435.   // switched tabs, and then switched back. We're still active, but don't
  1436.   // want to show the warning again. The user can cause it to show by
  1437.   // clicking our icon in the urlbar.
  1438.   if (this.messageShouldShow_)
  1439.     this.showMessage_();
  1440. }
  1441.  
  1442. /**
  1443.  * Invoked to display the warning message explicitly, for example if the user
  1444.  * clicked the url warning icon.
  1445.  */
  1446. PROT_PhishMsgDisplayerBase.prototype.explicitShow = function() {
  1447.   this.messageShouldShow_ = true;
  1448.   if (!this.messageShowing_)
  1449.     this.showMessage_();
  1450. }
  1451.  
  1452. /** 
  1453.  * Invoked by the browser view when our browser is switched away from
  1454.  */
  1455. PROT_PhishMsgDisplayerBase.prototype.browserUnselected = function() {
  1456.   this.removeWarningInUrlbar_();
  1457.   if (this.messageShowing_)
  1458.     this.hideMessage_();
  1459. }
  1460.  
  1461. /**
  1462.  * Invoked to make this displayer active. The displayer will now start
  1463.  * responding to notifications such as commands and resize events. We
  1464.  * can't do this in the constructor because there might be many 
  1465.  * displayers instantiated waiting in the problem queue for a particular
  1466.  * browser (e.g., a page has multiple framed problem pages), and we
  1467.  * don't want them all responding to commands!
  1468.  *
  1469.  * Invoked zero (the page we're a warning for was nav'd away from
  1470.  * before it reaches the head of the problem queue) or one (we're
  1471.  * displaying this warning) times by the browser view.
  1472.  */
  1473. PROT_PhishMsgDisplayerBase.prototype.start = function() {
  1474.   G_Assert(this, this.started_ == undefined, "Displayer started twice?");
  1475.   this.started_ = true;
  1476.  
  1477.   this.commandController_ = new PROT_CommandController(this.commandHandlers_);
  1478.   this.doc_.defaultView.controllers.appendController(this.commandController_);
  1479.  
  1480.   // Add an event listener for when the browser resizes (e.g., user
  1481.   // shows/hides the sidebar).
  1482.   this.resizeHandler_ = BindToObject(this.onBrowserResized_, this);
  1483.   this.browser_.addEventListener("resize",
  1484.                                  this.resizeHandler_, 
  1485.                                  false);
  1486. }
  1487.  
  1488. /**
  1489.  * @returns Boolean indicating whether this displayer is currently
  1490.  *          active
  1491.  */
  1492. PROT_PhishMsgDisplayerBase.prototype.isActive = function() {
  1493.   return !!this.started_;
  1494. }
  1495.  
  1496. /**
  1497.  * Invoked by the browser view to clean up after the user is done 
  1498.  * interacting with the message. Should be called once by the browser
  1499.  * view. 
  1500.  */
  1501. PROT_PhishMsgDisplayerBase.prototype.done = function() {
  1502.   G_Assert(this, !this.done_, "Called done more than once?");
  1503.   this.done_ = true;
  1504.  
  1505.   // If the Document we're showing the warning for was nav'd away from
  1506.   // before we had a chance to get started, we have nothing to do.
  1507.   if (this.started_) {
  1508.  
  1509.     // If we were started, we must be the current problem, so these things
  1510.     // must be showing
  1511.     this.removeWarningInUrlbar_();
  1512.  
  1513.     // Could be though that they've closed the warning dialog
  1514.     if (this.messageShowing_)
  1515.       this.hideMessage_();
  1516.  
  1517.     if (this.resizeHandler_) {
  1518.       this.browser_.removeEventListener("resize", 
  1519.                                         this.resizeHandler_, 
  1520.                                         false);
  1521.       this.resizeHandler_ = null;
  1522.     }
  1523.     
  1524.     var win = this.doc_.defaultView;
  1525.     win.controllers.removeController(this.commandController_);
  1526.     this.commandController_ = null;
  1527.   }
  1528. }
  1529.  
  1530. /**
  1531.  * Helper function to remove a substring from inside a string.
  1532.  *
  1533.  * @param orig String to remove substring from
  1534.  * 
  1535.  * @param toRemove String to remove (if it is present)
  1536.  *
  1537.  * @returns String with the substring removed
  1538.  */
  1539. PROT_PhishMsgDisplayerBase.prototype.removeIfExists_ = function(orig,
  1540.                                                                 toRemove) {
  1541.   var pos = orig.indexOf(toRemove);
  1542.   if (pos != -1)
  1543.     orig = orig.substring(0, pos) + orig.substring(pos + toRemove.length);
  1544.  
  1545.   return orig;
  1546. }
  1547.  
  1548. /**
  1549.  * This method makes our warning icon visible in the location bar. It will
  1550.  * be removed only when the problematic document is navigated awy from 
  1551.  * (i.e., when done() is called), and not when the warning is dismissed.
  1552.  */
  1553. PROT_PhishMsgDisplayerBase.prototype.addWarningInUrlbar_ = function() {
  1554.   var urlbarIcon = this.doc_.getElementById(this.urlbarIconId_);
  1555.   if (!urlbarIcon)
  1556.     return;
  1557.   urlbarIcon.setAttribute('level', 'warn');
  1558. }
  1559.  
  1560. /**
  1561.  * Hides our urlbar icon
  1562.  */
  1563. PROT_PhishMsgDisplayerBase.prototype.removeWarningInUrlbar_ = function() {
  1564.   var urlbarIcon = this.doc_.getElementById(this.urlbarIconId_);
  1565.   if (!urlbarIcon)
  1566.     return;
  1567.   urlbarIcon.setAttribute('level', 'safe');
  1568. }
  1569.  
  1570. /**
  1571.  * VIRTUAL -- Displays the warning message
  1572.  */
  1573. PROT_PhishMsgDisplayerBase.prototype.showMessage_ = function() { };
  1574.  
  1575. /**
  1576.  * VIRTUAL -- Hide the warning message from the user.
  1577.  */
  1578. PROT_PhishMsgDisplayerBase.prototype.hideMessage_ = function() { };
  1579.  
  1580. /**
  1581.  * Reposition the message relative to refElement in the parent window
  1582.  *
  1583.  * @param message Reference to the element to position
  1584.  * @param tail Reference to the message tail
  1585.  * @param refElement Reference to element relative to which we position
  1586.  *                   ourselves
  1587.  */
  1588. PROT_PhishMsgDisplayerBase.prototype.adjustLocation_ = function(message,
  1589.                                                                 tail,
  1590.                                                                 refElement) {
  1591.   var refX = refElement.boxObject.x;
  1592.   var refY = refElement.boxObject.y;
  1593.   var refHeight = refElement.boxObject.height;
  1594.   var refWidth = refElement.boxObject.width;
  1595.   G_Debug(this, "Ref element is at [window-relative] (" + refX + ", " + 
  1596.           refY + ")");
  1597.  
  1598.   var pixelsIntoRefY = -2;
  1599.   var tailY = refY + refHeight - pixelsIntoRefY;
  1600.   var tailPixelsLeftOfRefX = tail.boxObject.width;
  1601.   var tailPixelsIntoRefX = Math.round(refWidth / 2);
  1602.   var tailX = refX - tailPixelsLeftOfRefX + tailPixelsIntoRefX;
  1603.  
  1604.   // Move message up a couple pixels so the tail overlaps it.
  1605.   var messageY = tailY + tail.boxObject.height - 2;
  1606.   var messagePixelsLeftOfRefX = 375;
  1607.   var messageX = refX - messagePixelsLeftOfRefX;
  1608.   G_Debug(this, "Message is at [window-relative] (" + messageX + ", " + 
  1609.           messageY + ")");
  1610.   G_Debug(this, "Tail is at [window-relative] (" + tailX + ", " + 
  1611.           tailY + ")");
  1612.  
  1613.   if (messageX < 0) {
  1614.     // We're hanging off the left edge, switch to floating mode
  1615.     tail.style.display = "none";
  1616.     this.adjustLocationFloating_(message);
  1617.     return;
  1618.   }
  1619.  
  1620.   tail.style.top = tailY + "px";
  1621.   tail.style.left = tailX + "px";
  1622.   message.style.top = messageY + "px";
  1623.   message.style.left = messageX + "px";
  1624.   
  1625.   this.maybeAddScrollbars_();
  1626. }
  1627.  
  1628. /**
  1629.  * Position the warning bubble with no reference element.  In this case we
  1630.  * just center the warning bubble at the top of the users window.
  1631.  * @param message XULElement message bubble XUL container.
  1632.  */
  1633. PROT_PhishMsgDisplayerBase.prototype.adjustLocationFloating_ = function(message) {
  1634.   // Compute X offset
  1635.   var browserX = this.browser_.boxObject.x;
  1636.   var browserXCenter = browserX + this.browser_.boxObject.width / 2;
  1637.   var messageX = browserXCenter - (message.boxObject.width / 2);
  1638.  
  1639.   // Compute Y offset (top of the browser window)
  1640.   var messageY = this.browser_.boxObject.y;
  1641.  
  1642.   // Position message
  1643.   message.style.top = messageY + "px";
  1644.   message.style.left = messageX + "px";
  1645.  
  1646.   this.maybeAddScrollbars_();
  1647. }
  1648.  
  1649. /**
  1650.  * Add a vertical scrollbar if we're falling out of the browser window.
  1651.  */
  1652. PROT_PhishMsgDisplayerBase.prototype.maybeAddScrollbars_ = function() {
  1653.   var message = this.doc_.getElementById(this.messageId_);
  1654.   
  1655.   var content = this.doc_.getElementById(this.messageContentId_);
  1656.   var bottom = content.boxObject.y + content.boxObject.height;
  1657.   var maxY = this.doc_.defaultView.innerHeight;
  1658.   G_Debug(this, "bottom: " + bottom + ", maxY: " + maxY
  1659.                 + ", new height: " + (maxY - content.boxObject.y));
  1660.   if (bottom > maxY) {
  1661.     var newHeight = maxY - content.boxObject.y;
  1662.     if (newHeight < 1)
  1663.       newHeight = 1;
  1664.  
  1665.     content.style.height = newHeight + "px";
  1666.     content.style.overflow = "auto";
  1667.   }
  1668. }
  1669.  
  1670. /**
  1671.  * Show the extended warning message
  1672.  */
  1673. PROT_PhishMsgDisplayerBase.prototype.showMore_ = function() {
  1674.   this.doc_.getElementById(this.extendedMessageId_).hidden = false;
  1675.   this.doc_.getElementById(this.showmoreLinkId_).style.display = "none";
  1676.  
  1677.   // set FAQ URL
  1678.   var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  1679.                             .getService(Components.interfaces.nsIURLFormatter);
  1680.   var faqURL = formatter.formatURLPref("browser.safebrowsing.warning.infoURL");
  1681.   var labelEl = this.doc_.getElementById(this.faqLinkId_);
  1682.   labelEl.setAttribute("href", faqURL);
  1683.   
  1684.   this.maybeAddScrollbars_();
  1685. }
  1686.  
  1687. /**
  1688.  * The user clicked on one of the links in the buble.  Display the
  1689.  * corresponding page in a new window with all the chrome enabled.
  1690.  *
  1691.  * @param url The URL to display in a new window
  1692.  */
  1693. PROT_PhishMsgDisplayerBase.prototype.showURL_ = function(url) {
  1694.   this.windowWatcher_.openWindow(this.windowWatcher_.activeWindow,
  1695.                                  url,
  1696.                                  "_blank",
  1697.                                  null,
  1698.                                  null);
  1699. }
  1700.  
  1701. /**
  1702.  * If the warning bubble came up in error, this url goes to a form
  1703.  * to notify the data provider.
  1704.  * @return url String
  1705.  */
  1706. PROT_PhishMsgDisplayerBase.prototype.getReportErrorURL_ = function() {
  1707.   var badUrl = this.url_;
  1708.  
  1709.   var url = gDataProvider.getReportErrorURL();
  1710.   url += "&url=" + encodeURIComponent(badUrl);
  1711.   return url;
  1712. }
  1713.  
  1714. /**
  1715.  * URL for the user to report back to us.  This is to provide the user
  1716.  * with an action after being warned.
  1717.  */
  1718. PROT_PhishMsgDisplayerBase.prototype.getReportGenericURL_ = function() {
  1719.   var badUrl = this.url_;
  1720.  
  1721.   var url = gDataProvider.getReportGenericURL();
  1722.   url += "&url=" + encodeURIComponent(badUrl);
  1723.   return url;
  1724. }
  1725.  
  1726.  
  1727. /**
  1728.  * A specific implementation of the dislpayer using a canvas. This
  1729.  * class is meant for use on platforms that don't support transparent
  1730.  * elements over browser content (currently: all platforms). 
  1731.  *
  1732.  * The main ugliness is the fact that we're hiding the content area and
  1733.  * painting the page to canvas. As a result, we must periodically
  1734.  * re-paint the canvas to reflect updates to the page. Otherwise if
  1735.  * the page was half-loaded when we showed our warning, it would
  1736.  * stay that way even though the page actually finished loading. 
  1737.  *
  1738.  * See base constructor for full details of constructor args.
  1739.  *
  1740.  * @constructor
  1741.  */
  1742. function PROT_PhishMsgDisplayerCanvas(msgDesc, browser, doc, url) {
  1743.   PROT_PhishMsgDisplayerBase.call(this, msgDesc, browser, doc, url);
  1744.  
  1745.   this.dimAreaId_ = "safebrowsing-dim-area-canvas";
  1746.   this.pageCanvasId_ = "safebrowsing-page-canvas";
  1747.   this.xhtmlNS_ = "http://www.w3.org/1999/xhtml";     // we create html:canvas
  1748. }
  1749.  
  1750. PROT_PhishMsgDisplayerCanvas.inherits(PROT_PhishMsgDisplayerBase);
  1751.  
  1752. /**
  1753.  * Displays the warning message.  First we make sure the overlay is loaded
  1754.  * then call showMessageAfterOverlay_.
  1755.  */
  1756. PROT_PhishMsgDisplayerCanvas.prototype.showMessage_ = function() { }
  1757.  
  1758. /**
  1759.  * This does the actual work of showing the warning message.
  1760.  */
  1761. PROT_PhishMsgDisplayerCanvas.prototype.showMessageAfterOverlay_ = function() {
  1762.   this.messageShowing_ = true;
  1763.  
  1764.   // Position the canvas overlay. Order here is significant, but don't ask me
  1765.   // why for some of these. You need to:
  1766.   // 1. get browser dimensions
  1767.   // 2. add canvas to the document
  1768.   // 3. unhide the dimmer (gray out overlay)
  1769.   // 4. display to the canvas
  1770.   // 5. unhide the warning message
  1771.   // 6. update link targets in warning message
  1772.   // 7. focus the warning message
  1773.  
  1774.   // (1)
  1775.   var w = this.browser_.boxObject.width;
  1776.   G_Debug(this, "browser w=" + w);
  1777.   var h = this.browser_.boxObject.height;
  1778.   G_Debug(this, "browser h=" + h);
  1779.   var x = this.browser_.boxObject.x;
  1780.   G_Debug(this, "browser x=" + w);
  1781.   var y = this.browser_.boxObject.y;
  1782.   G_Debug(this, "browser y=" + h);
  1783.  
  1784.   var win = this.browser_.contentWindow;
  1785.   var scrollX = win.scrollX;
  1786.   G_Debug(this, "win scrollx=" + scrollX);
  1787.   var scrollY = win.scrollY;
  1788.   G_Debug(this, "win scrolly=" + scrollY);
  1789.  
  1790.   // (2)
  1791.   // We add the canvas dynamically and remove it when we're done because
  1792.   // leaving it hanging around consumes a lot of memory.
  1793.   var pageCanvas = this.doc_.createElementNS(this.xhtmlNS_, "html:canvas");
  1794.   pageCanvas.id = this.pageCanvasId_;
  1795.   pageCanvas.style.left = x + 'px';
  1796.   pageCanvas.style.top = y + 'px';
  1797.  
  1798.   var dimarea = this.doc_.getElementById(this.dimAreaId_);
  1799.   this.doc_.getElementById('main-window').insertBefore(pageCanvas,
  1800.                                                        dimarea);
  1801.  
  1802.   // (3)
  1803.   dimarea.style.left = x + 'px';
  1804.   dimarea.style.top = y + 'px';
  1805.   dimarea.style.width = w + 'px';
  1806.   dimarea.style.height = h + 'px';
  1807.   dimarea.hidden = false;
  1808.   
  1809.   // (4)
  1810.   pageCanvas.setAttribute("width", w);
  1811.   pageCanvas.setAttribute("height", h);
  1812.  
  1813.   var bgcolor = this.getBackgroundColor_();
  1814.  
  1815.   var cx = pageCanvas.getContext("2d");
  1816.   cx.drawWindow(win, scrollX, scrollY, w, h, bgcolor);
  1817.  
  1818.   // Now repaint the window every so often in case the content hasn't fully
  1819.   // loaded at this point.
  1820.   var debZone = this.debugZone;
  1821.   function repaint() {
  1822.     G_Debug(debZone, "Repainting canvas...");
  1823.     cx.drawWindow(win, scrollX, scrollY, w, h, bgcolor);
  1824.   };
  1825.   this.repainter_ = new PROT_PhishMsgCanvasRepainter(repaint);
  1826.  
  1827.   // (5)
  1828.   this.showAndPositionWarning_();
  1829.  
  1830.   // (6)
  1831.   var link = this.doc_.getElementById('safebrowsing-palm-falsepositive-link');
  1832.   link.href = this.getReportErrorURL_();
  1833.  
  1834.   // (7)
  1835.   this.doc_.getElementById(this.messageContentId_).focus();
  1836. }
  1837.  
  1838. /**
  1839.  * Show and position the warning message.  We position the waring message
  1840.  * relative to the icon in the url bar, but if the element doesn't exist,
  1841.  * (e.g., the user remove the url bar from her/his chrome), we anchor at the
  1842.  * top of the window.
  1843.  */
  1844. PROT_PhishMsgDisplayerCanvas.prototype.showAndPositionWarning_ = function() {
  1845.   var refElement = this.doc_.getElementById(this.refElementId_);
  1846.   var message = this.doc_.getElementById(this.messageId_);
  1847.   var tail = this.doc_.getElementById(this.messageTailId_);
  1848.  
  1849.   message.hidden = false;
  1850.   message.style.display = "block";
  1851.  
  1852.   // Determine if the refElement is visible.
  1853.   if (this.isVisibleElement_(refElement)) {
  1854.     // Show tail and position warning relative to refElement.
  1855.     tail.hidden = false;
  1856.     tail.style.display = "block";
  1857.     this.adjustLocation_(message, tail, refElement);
  1858.   } else {
  1859.     // No ref element, position in the top center of window.
  1860.     tail.hidden = true;
  1861.     tail.style.display = "none";
  1862.     this.adjustLocationFloating_(message);
  1863.   }
  1864. }
  1865.  
  1866. /**
  1867.  * @return Boolean true if elt is a visible XUL element.
  1868.  */
  1869. PROT_PhishMsgDisplayerCanvas.prototype.isVisibleElement_ = function(elt) {
  1870.   if (!elt)
  1871.     return false;
  1872.   
  1873.   // If it's on a collapsed/hidden toolbar, the x position is set to 0.
  1874.   if (elt.boxObject.x == 0)
  1875.     return false;
  1876.  
  1877.   return true;
  1878. }
  1879.  
  1880. /**
  1881.  * Hide the warning message from the user.
  1882.  */
  1883. PROT_PhishMsgDisplayerCanvas.prototype.hideMessage_ = function() { }
  1884.  
  1885.  
  1886. /**
  1887.  * Helper class that periodically repaints the canvas. We repaint
  1888.  * frequently at first, and then back off to a less frequent schedule
  1889.  * at "steady state," and finally just stop altogether. We have to do
  1890.  * this because we're not sure if the page has finished loading when
  1891.  * we first paint the canvas, and because we want to reflect any
  1892.  * dynamically written content into the canvas as it appears in the
  1893.  * page after load.
  1894.  *
  1895.  * @param repaintFunc Function to call to repaint browser.
  1896.  *
  1897.  * @constructor
  1898.  */
  1899. function PROT_PhishMsgCanvasRepainter(repaintFunc) {
  1900.   this.count_ = 0;
  1901.   this.repaintFunc_ = repaintFunc;
  1902.   this.initPeriodMS_ = 500;             // Initially repaint every 500ms
  1903.   this.steadyStateAtMS_ = 10 * 1000;    // Go slowly after 10 seconds,
  1904.   this.steadyStatePeriodMS_ = 3 * 1000; // repainting every 3 seconds, and
  1905.   this.quitAtMS_ = 20 * 1000;           // stop after 20 seconds
  1906.   this.startMS_ = (new Date).getTime();
  1907.   this.alarm_ = new G_Alarm(BindToObject(this.repaint, this), 
  1908.                             this.initPeriodMS_);
  1909. }
  1910.  
  1911. /**
  1912.  * Called periodically to repaint the canvas
  1913.  */
  1914. PROT_PhishMsgCanvasRepainter.prototype.repaint = function() {
  1915.   this.repaintFunc_();
  1916.  
  1917.   var nextRepaint;
  1918.   // If we're in "steady state", use the slow repaint rate, else fast
  1919.   if ((new Date).getTime() - this.startMS_ > this.steadyStateAtMS_)
  1920.     nextRepaint = this.steadyStatePeriodMS_;
  1921.   else 
  1922.     nextRepaint = this.initPeriodMS_;
  1923.  
  1924.   if (!((new Date).getTime() - this.startMS_ > this.quitAtMS_))
  1925.     this.alarm_ = new G_Alarm(BindToObject(this.repaint, this), nextRepaint);
  1926. }
  1927.  
  1928. /**
  1929.  * Called to stop repainting the canvas
  1930.  */
  1931. PROT_PhishMsgCanvasRepainter.prototype.cancel = function() {
  1932.   if (this.alarm_) {
  1933.     this.alarm_.cancel();
  1934.     this.alarm_ = null;
  1935.   }
  1936.   this.repaintFunc_ = null;
  1937. }
  1938. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\phishing-warden.js"
  1939.  
  1940.  
  1941. // The warden checks request to see if they are for phishy pages. It
  1942. // does so by either querying a remote server with the URL (advanced
  1943. // protectoin mode) or querying our locally stored blacklists (privacy
  1944. // mode).
  1945. // 
  1946. // When the warden notices a problem, it queries all browser views
  1947. // (each of which corresopnds to an open browser window) to see
  1948. // whether one of them can handle it. A browser view can handle a
  1949. // problem if its browser window has an HTMLDocument loaded with the
  1950. // given URL and that Document hasn't already been flagged as a
  1951. // problem. For every problematic URL we notice loading, at most one
  1952. // Document is flagged as problematic. Otherwise you can get into
  1953. // trouble if multiple concurrent phishy pages load with the same URL.
  1954. //
  1955. // Since we check URLs very early in the request cycle (in a progress
  1956. // listener), the URL might not yet be associated with a Document when
  1957. // we determine that it is phishy. So the the warden retries finding
  1958. // a browser view to handle the problem until one can, or until it
  1959. // determines it should give up (see complicated logic below).
  1960. //
  1961. // The warden has displayers that the browser view uses to render
  1962. // different kinds of warnings (e.g., one that's shown before a page
  1963. // loads as opposed to one that's shown after the page has already
  1964. // loaded).
  1965. //
  1966. // Note: There is a single warden for the whole application.
  1967. //
  1968. // TODO better way to expose displayers/views to browser view
  1969.  
  1970. const kPhishWardenEnabledPref = "browser.safebrowsing.enabled";
  1971. const kPhishWardenRemoteLookups = "browser.safebrowsing.remoteLookups";
  1972.  
  1973. // We have hardcoded URLs that we let people navigate to in order to 
  1974. // check out the warning.
  1975. const kTestUrls = {
  1976.   "http://www.google.com/tools/firefox/safebrowsing/phish-o-rama.html": true,
  1977.   "http://www.mozilla.org/projects/bonecho/anti-phishing/its-a-trap.html": true,
  1978.   "http://www.mozilla.com/firefox/its-a-trap.html": true,
  1979. }
  1980.  
  1981. /**
  1982.  * Abtracts the checking of user/browser actions for signs of
  1983.  * phishing. 
  1984.  *
  1985.  * @param progressListener nsIDocNavStartProgressListener
  1986.  * @param tabbrowser XUL tabbrowser element
  1987.  * @constructor
  1988.  */
  1989. function PROT_PhishingWarden(progressListener, tabbrowser) {
  1990.   PROT_ListWarden.call(this);
  1991.  
  1992.   this.debugZone = "phishwarden";
  1993.   this.testing_ = false;
  1994.   this.browserViews_ = [];
  1995.  
  1996.   // Use this to query preferences
  1997.   this.prefs_ = new G_Preferences();
  1998.  
  1999.   // Only one displayer so far; perhaps we'll have others in the future
  2000.   this.displayers_ = {
  2001.     "afterload": PROT_PhishMsgDisplayer,
  2002.   };
  2003.  
  2004.   // We use this dude to do lookups on our remote server
  2005.   this.fetcher_ = new PROT_TRFetcher();
  2006.  
  2007.   // We need to know whether we're enabled and whether we're in advanced
  2008.   // mode, so reflect the appropriate preferences into our state.
  2009.  
  2010.   // Read state: should we be checking remote preferences?
  2011.   this.checkRemote_ = this.prefs_.getPref(kPhishWardenRemoteLookups, null);
  2012.   
  2013.   // true if we should use whitelists to suppress remote lookups
  2014.   this.checkWhitelists_ = false;
  2015.  
  2016.   // Get notifications when the remote check preference changes
  2017.   var checkRemotePrefObserver = BindToObject(this.onCheckRemotePrefChanged,
  2018.                                              this);
  2019.   this.prefs_.addObserver(kPhishWardenRemoteLookups, checkRemotePrefObserver);
  2020.  
  2021.   // Global preference to enable the phishing warden
  2022.   this.phishWardenEnabled_ = this.prefs_.getPref(kPhishWardenEnabledPref, null);
  2023.  
  2024.   // Get notifications when the phishing warden enabled pref changes
  2025.   var phishWardenPrefObserver = 
  2026.     BindToObject(this.onPhishWardenEnabledPrefChanged, this);
  2027.   this.prefs_.addObserver(kPhishWardenEnabledPref, phishWardenPrefObserver);
  2028.   
  2029.   // Get notifications when the data provider pref changes
  2030.   var dataProviderPrefObserver =
  2031.     BindToObject(this.onDataProviderPrefChanged, this);
  2032.   this.prefs_.addObserver(kDataProviderIdPref, dataProviderPrefObserver);
  2033.  
  2034.   // Hook up our tab open/close events which in turn add our web progress
  2035.   // listener to each browser tab.
  2036.   this.progressListener_ = progressListener;
  2037.   this.progressListener_.callback = this;
  2038.   this.tabbrowser_ = tabbrowser;
  2039.   tabbrowser.mTabBox.addEventListener("TabOpen",
  2040.                                       BindToObject(this.onTabOpen_, this),
  2041.                                       false);
  2042.   tabbrowser.mTabBox.addEventListener("TabClose",
  2043.                                       BindToObject(this.onTabClose_, this),
  2044.                                       false);
  2045.   if (this.phishWardenEnabled_) {
  2046.     // Since we already missed the TabOpen events for existing tabs, set those
  2047.     // up now.
  2048.     this.addWebProgressToAllTabs_();
  2049.   }
  2050.  
  2051.   // ms to wait after a request has started before firing JS callback
  2052.   this.progressListener_.delay = 1500;
  2053.  
  2054.   // object to keep track of request errors if we're in remote check mode
  2055.   this.requestBackoff_ = new RequestBackoff(3 /* num errors */,
  2056.                                    10*60*1000 /* error time, 10min */,
  2057.                                    10*60*1000 /* backoff interval, 10min */,
  2058.                                    6*60*60*1000 /* max backoff, 6hr */);
  2059.  
  2060.   G_Debug(this, "phishWarden initialized");
  2061. }
  2062.  
  2063. PROT_PhishingWarden.inherits(PROT_ListWarden);
  2064.  
  2065. /**
  2066.  * We implement nsIWebProgressListener
  2067.  */
  2068. PROT_PhishingWarden.prototype.QueryInterface = function(iid) {
  2069.   if (iid.equals(Ci.nsISupports) || 
  2070.       iid.equals(Ci.nsIWebProgressListener) ||
  2071.       iid.equals(Ci.nsISupportsWeakReference))
  2072.     return this;
  2073.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2074. }
  2075.  
  2076. /**
  2077.  * Cleanup on shutdown.
  2078.  */
  2079. PROT_PhishingWarden.prototype.shutdown = function() {
  2080.   this.prefs_.removeAllObservers();
  2081.   this.progressListener_.callback = null;
  2082.   this.progressListener_ = null;
  2083.   this.listManager_ = null;
  2084. }
  2085.  
  2086. /**
  2087.  * When a preference (either advanced features or the phishwarden
  2088.  * enabled) changes, we might have to start or stop asking for updates. 
  2089.  * 
  2090.  * This is a little tricky; we start or stop management only when we
  2091.  * have complete information we can use to determine whether we
  2092.  * should.  It could be the case that one pref or the other isn't set
  2093.  * yet (e.g., they haven't opted in/out of advanced features). So do
  2094.  * nothing unless we have both pref values -- we get notifications for
  2095.  * both, so eventually we will start correctly.
  2096.  */ 
  2097. PROT_PhishingWarden.prototype.maybeToggleUpdateChecking = function() {
  2098.   if (this.testing_)
  2099.     return;
  2100.  
  2101.   var phishWardenEnabled = this.prefs_.getPref(kPhishWardenEnabledPref, null);
  2102.  
  2103.   this.checkRemote_ = this.prefs_.getPref(kPhishWardenRemoteLookups, null);
  2104.  
  2105.   G_Debug(this, "Maybe toggling update checking. " +
  2106.           "Warden enabled? " + phishWardenEnabled + " || " +
  2107.           "Check remote? " + this.checkRemote_);
  2108.  
  2109.   // Do nothing unless both prefs are set.  They can be null (unset), true, or
  2110.   // false.
  2111.   if (phishWardenEnabled === null || this.checkRemote_ === null)
  2112.     return;
  2113.  
  2114.   // We update and save to disk all tables if we don't have remote checking
  2115.   // enabled.
  2116.   if (phishWardenEnabled === true) {
  2117.     // If anti-phishing is enabled, we always download the local files to
  2118.     // use in case remote lookups fail.
  2119.     this.enableBlacklistTableUpdates();
  2120.     this.enableWhitelistTableUpdates();
  2121.  
  2122.     if (this.checkRemote_ === true) {
  2123.       // Remote lookup mode
  2124.       // We check to see if the local list update host is the same as the
  2125.       // remote lookup host.  If they are the same, then we don't bother
  2126.       // to do a remote url check if the url is in the whitelist.
  2127.       var ioService = Cc["@mozilla.org/network/io-service;1"]
  2128.                      .getService(Ci.nsIIOService);
  2129.       var updateHost = '';
  2130.       var lookupHost = '';
  2131.       try {
  2132.         var url = ioService.newURI(gDataProvider.getUpdateURL(),
  2133.                                          null, null);
  2134.         updateHost = url.asciiHost;
  2135.       } catch (e) { }
  2136.       try {
  2137.         var url = ioService.newURI(gDataProvider.getLookupURL(),
  2138.                                          null, null);
  2139.         lookupHost = url.asciiHost;
  2140.       } catch (e) { }
  2141.  
  2142.       if (updateHost && lookupHost && updateHost == lookupHost) {
  2143.         // The data provider for local lists and remote lookups is the
  2144.         // same, enable whitelist lookup suppression.
  2145.         this.checkWhitelists_ = true;
  2146.       } else {
  2147.         // hosts don't match, don't use whitelist suppression
  2148.         this.checkWhitelists_ = false;
  2149.       }
  2150.     }
  2151.   } else {
  2152.     // Anti-phishing is off, disable table updates
  2153.     this.disableBlacklistTableUpdates();
  2154.     this.disableWhitelistTableUpdates();
  2155.   }
  2156. }
  2157.  
  2158. /**
  2159.  * Controllers register their browser views with us
  2160.  *
  2161.  * @param view Reference to a browser view 
  2162.  */
  2163. PROT_PhishingWarden.prototype.addBrowserView = function(view) {
  2164.   G_Debug(this, "New browser view registered.");
  2165.   this.browserViews_.push(view);
  2166. }
  2167.  
  2168. /**
  2169.  * Controllers unregister their views when their window closes
  2170.  *
  2171.  * @param view Reference to a browser view 
  2172.  */
  2173. PROT_PhishingWarden.prototype.removeBrowserView = function(view) {
  2174.   for (var i = 0; i < this.browserViews_.length; i++)
  2175.     if (this.browserViews_[i] === view) {
  2176.       G_Debug(this, "Browser view unregistered.");
  2177.       this.browserViews_.splice(i, 1);
  2178.       return;
  2179.     }
  2180.   G_Assert(this, false, "Tried to unregister non-existent browser view!");
  2181. }
  2182.  
  2183. /**
  2184.  * Deal with a user changing the pref that says whether we should check
  2185.  * the remote server (i.e., whether we're in advanced mode)
  2186.  *
  2187.  * @param prefName Name of the pref holding the value indicating whether
  2188.  *                 we should check remote server
  2189.  */
  2190. PROT_PhishingWarden.prototype.onCheckRemotePrefChanged = function(prefName) {
  2191.   this.checkRemote_ = this.prefs_.getPref(prefName, this.checkRemote_);
  2192.   this.requestBackoff_.reset();
  2193.   this.maybeToggleUpdateChecking();
  2194. }
  2195.  
  2196. /**
  2197.  * Deal with a user changing the pref that says whether we should 
  2198.  * enable the phishing warden (i.e., that SafeBrowsing is active)
  2199.  *
  2200.  * @param prefName Name of the pref holding the value indicating whether
  2201.  *                 we should enable the phishing warden
  2202.  */
  2203. PROT_PhishingWarden.prototype.onPhishWardenEnabledPrefChanged = function(
  2204.                                                                     prefName) {
  2205.   // Just to be safe, ignore changes to sub prefs.
  2206.   if (prefName != "browser.safebrowsing.enabled")
  2207.     return;
  2208.  
  2209.   this.phishWardenEnabled_ = 
  2210.     this.prefs_.getPref(prefName, this.phishWardenEnabled_);
  2211.   this.requestBackoff_.reset();
  2212.   this.maybeToggleUpdateChecking();
  2213.  
  2214.   // Update the progress listeners.
  2215.   if (this.phishWardenEnabled_) {
  2216.     this.addWebProgressToAllTabs_();
  2217.   } else {
  2218.     for (var i = 0, tab = null; tab = this.tabbrowser_.mTabs[i]; ++i) {
  2219.       var browser = tab.linkedBrowser;
  2220.       browser.webProgress.removeProgressListener(this.progressListener_);
  2221.     }
  2222.   }
  2223. }
  2224.  
  2225. /**
  2226.  * Event fired when the user changes data providers.
  2227.  */
  2228. PROT_PhishingWarden.prototype.onDataProviderPrefChanged = function(prefName) {
  2229.   // We want to reset request backoff state since it's a different provider.
  2230.   this.requestBackoff_.reset();
  2231.  
  2232.   // If we have a new data provider and we're doing remote lookups, then
  2233.   // we may want to use whitelist lookup suppression or change which
  2234.   // tables are being downloaded.
  2235.   if (this.checkRemote_) {
  2236.     this.maybeToggleUpdateChecking();
  2237.   }
  2238. }
  2239.  
  2240. /**
  2241.  * Event handler for new tab creation.  Make the web progress listener aware
  2242.  * of the new tab.
  2243.  */
  2244. PROT_PhishingWarden.prototype.onTabOpen_ = function(event) {
  2245.   if (!this.phishWardenEnabled_)
  2246.     return;
  2247.   var browser = event.target.linkedBrowser;
  2248.   browser.webProgress.addProgressListener(this.progressListener_,
  2249.                                           Ci.nsIWebProgress.NOTIFY_LOCATION);
  2250. }
  2251.  
  2252. /**
  2253.  * Event handler for tab closing.  Remove the web progress listener we
  2254.  * added earlier.
  2255.  */
  2256. PROT_PhishingWarden.prototype.onTabClose_ = function(event) {
  2257.   if (!this.phishWardenEnabled_)
  2258.     return;
  2259.   var browser = event.target.linkedBrowser;
  2260.   browser.webProgress.removeProgressListener(this.progressListener_);
  2261. }
  2262.  
  2263. /**
  2264.  * Add the web progress listener to all open tabs for this chrome window.
  2265.  */
  2266. PROT_PhishingWarden.prototype.addWebProgressToAllTabs_ = function() {
  2267.   for (var i = 0, tab = null; tab = this.tabbrowser_.mTabs[i]; ++i) {
  2268.     var browser = tab.linkedBrowser;
  2269.     browser.webProgress.addProgressListener(this.progressListener_,
  2270.                                          Ci.nsIWebProgress.NOTIFY_LOCATION);
  2271.   }
  2272. }
  2273.  
  2274. /**
  2275.  * A request for a Document has been initiated somewhere. Check it!
  2276.  *
  2277.  * @param request
  2278.  * @param url
  2279.  */ 
  2280. PROT_PhishingWarden.prototype.onDocNavStart = function(request, url) {
  2281.   G_Debug(this, "checkRemote: " +
  2282.           (this.checkRemote_ ? "yes" : "no"));
  2283.  
  2284.   // If we're on a test page, trigger the warning.
  2285.   // XXX Do we still need a test url or should each provider just put
  2286.   // it in their local list?
  2287.   if (this.isBlacklistTestURL(url)) {
  2288.     this.houstonWeHaveAProblem_(request);
  2289.     return;
  2290.   }
  2291.  
  2292.   // Make a remote lookup check if the pref is selected and if we haven't
  2293.   // triggered server backoff.  Otherwise, make a local check.
  2294.   if (this.checkRemote_ && this.requestBackoff_.canMakeRequest()) {
  2295.     // If we can use whitelists to suppress remote lookups, do so.
  2296.     if (this.checkWhitelists_) {
  2297.       var maybeRemoteCheck = BindToObject(this.maybeMakeRemoteCheck_,
  2298.                                           this,
  2299.                                           url,
  2300.                                           request);
  2301.       this.isWhiteURL(url, maybeRemoteCheck);
  2302.     } else {
  2303.       // Do a remote lookup (don't check whitelists)
  2304.       this.fetcher_.get(url,
  2305.                         BindToObject(this.onTRFetchComplete,
  2306.                                      this,
  2307.                                      url,
  2308.                                      request));
  2309.     }
  2310.   } else {
  2311.     // Check the local lists for a match.
  2312.     var evilCallback = BindToObject(this.localListMatch_,
  2313.                                     this,
  2314.                                     url,
  2315.                                     request);
  2316.     this.isEvilURL(url, evilCallback);
  2317.   }
  2318. }
  2319.  
  2320. /** 
  2321.  * Callback from whitelist check when remote lookups is on.
  2322.  * @param url String url to lookup
  2323.  * @param request nsIRequest object
  2324.  * @param status int enum from callback (PROT_ListWarden.IN_BLACKLIST,
  2325.  *    PROT_ListWarden.IN_WHITELIST, PROT_ListWarden.NOT_FOUND)
  2326.  */
  2327. PROT_PhishingWarden.prototype.maybeMakeRemoteCheck_ = function(url, request, status) {
  2328.   if (PROT_ListWarden.IN_WHITELIST == status)
  2329.     return;
  2330.  
  2331.   G_Debug(this, "Local whitelist lookup failed");
  2332.   this.fetcher_.get(url,
  2333.                     BindToObject(this.onTRFetchComplete,
  2334.                                  this,
  2335.                                  url,
  2336.                                  request));
  2337. }
  2338.  
  2339. /**
  2340.  * Invoked with the result of a lookupserver request.
  2341.  *
  2342.  * @param url String the URL we looked up
  2343.  * @param request The nsIRequest in which we're interested
  2344.  * @param trValues Object holding name/value pairs parsed from the
  2345.  *                 lookupserver's response
  2346.  * @param status Number HTTP status code or NS_ERROR_NOT_AVAILABLE if there's
  2347.  *               an HTTP error
  2348.  */
  2349. PROT_PhishingWarden.prototype.onTRFetchComplete = function(url,
  2350.                                                            request,
  2351.                                                            trValues,
  2352.                                                            status) {
  2353.   // Did the remote http request succeed?  If not, we fall back on
  2354.   // local lists.
  2355.   if (status == Components.results.NS_ERROR_NOT_AVAILABLE ||
  2356.       this.requestBackoff_.isErrorStatus_(status)) {
  2357.     this.requestBackoff_.noteServerResponse(status);
  2358.  
  2359.     G_Debug(this, "remote check failed, using local lists instead");
  2360.     var evilCallback = BindToObject(this.localListMatch_,
  2361.                                     this,
  2362.                                     url,
  2363.                                     request);
  2364.     this.isEvilURL(url, evilCallback);
  2365.   } else {
  2366.     var callback = BindToObject(this.houstonWeHaveAProblem_, this, request);
  2367.     this.checkRemoteData(callback, trValues);
  2368.   }
  2369. }
  2370.  
  2371. /**
  2372.  * One of our Check* methods found a problem with a request. Why do we
  2373.  * need to keep the nsIRequest (instead of just passing in the URL)? 
  2374.  * Because we need to know when to stop looking for the URL it's
  2375.  * fetching, and to know this we need the nsIRequest.isPending flag.
  2376.  *
  2377.  * @param request nsIRequest that is problematic
  2378.  */
  2379. PROT_PhishingWarden.prototype.houstonWeHaveAProblem_ = function(request) {
  2380.  
  2381.   // We have a problem request that might or might not be associated
  2382.   // with a Document that's currently in a browser. If it is, we 
  2383.   // want that Document. If it's not, we want to give it a chance to 
  2384.   // be loaded. See below for complete details.
  2385.  
  2386.   if (this.maybeLocateProblem_(request))       // Cases 1 and 2 (see below)
  2387.     return;
  2388.  
  2389.   // OK, so the request isn't associated with any currently accessible
  2390.   // Document, and we want to give it the chance to be. We don't want
  2391.   // to retry forever (e.g., what if the Document was already displayed
  2392.   // and navigated away from?), so we'll use nsIRequest.isPending to help
  2393.   // us decide what to do.
  2394.   //
  2395.   // Aácomplication arises because there is a lag between when a
  2396.   // request transitions from pending to not-pending and when it's
  2397.   // associated with a Document in a browser. The transition from
  2398.   // pending to not occurs just before the notification corresponding
  2399.   // to NavWatcher.DOCNAVSTART (see NavWatcher), but the association
  2400.   // occurs afterwards. Unfortunately, we're probably in DOCNAVSTART.
  2401.   // 
  2402.   // Diagnosis by Darin:
  2403.   // ---------------------------------------------------------------------------
  2404.   // Here's a summary of what happens:
  2405.   //
  2406.   //   RestorePresentation() {
  2407.   //     Dispatch_OnStateChange(dummy_request, STATE_START)
  2408.   //     PostCompletionEvent()
  2409.   //   }
  2410.   //
  2411.   //   CompletionEvent() {
  2412.   //     ReallyRestorePresentation()
  2413.   //     Dispatch_OnStateChange(dummy_request, STATE_STOP)
  2414.   //   }
  2415.   //
  2416.   // So, now your code receives that initial OnStateChange event and sees
  2417.   // that the dummy_request is not pending and not loaded in any window.
  2418.   // So, you put a timeout(0) event in the queue.  Then, the CompletionEvent
  2419.   // is added to the queue.  The stack unwinds....
  2420.   //
  2421.   // Your timeout runs, and you find that the dummy_request is still not
  2422.   // pending and not loaded in any window.  Then the CompletionEvent
  2423.   // runs, and it hooks up the cached presentation.
  2424.   // 
  2425.   // https://bugzilla.mozilla.org/show_bug.cgi?id=319527
  2426.   // ---------------------------------------------------------------------------
  2427.   //
  2428.   // So the logic is:
  2429.   //
  2430.   //         request     found an unhandled          
  2431.   //  case   pending?    doc with the url?         action
  2432.   //  ----------------------------------------------------------------
  2433.   //   1      yes             yes           Use that doc (handled above)
  2434.   //   2      no              yes           Use that doc (handled above)
  2435.   //   3      yes             no            Retry
  2436.   //   4      no              no            Retry twice (case described above)
  2437.   //
  2438.   // We don't get into trouble with Docs with the same URL "stealing" the 
  2439.   // warning because there is exactly one warning signaled per nav to 
  2440.   // a problem URL, and each Doc can be marked as problematic at most once.
  2441.  
  2442.   if (request.isPending()) {        // Case 3
  2443.  
  2444.     G_Debug(this, "Can't find problem Doc; Req pending. Retrying.");
  2445.     new G_Alarm(BindToObject(this.houstonWeHaveAProblem_, 
  2446.                              this, 
  2447.                              request), 
  2448.                 200 /*ms*/);
  2449.  
  2450.   } else {                          // Case 4
  2451.  
  2452.     G_Debug(this, 
  2453.             "Can't find problem Doc; Req completed. Retrying at most twice.");
  2454.     new G_ConditionalAlarm(BindToObject(this.maybeLocateProblem_, 
  2455.                                         this, 
  2456.                                         request),
  2457.                            0 /* next event loop */, 
  2458.                            true /* repeat */, 
  2459.                            2 /* at most twice */);
  2460.   }
  2461. }
  2462.  
  2463. /**
  2464.  * Query all browser views we know about and offer them the chance to
  2465.  * handle the problematic request.
  2466.  *
  2467.  * @param request nsIRequest that is problematic
  2468.  * 
  2469.  * @returns Boolean indicating if someone decided to handle it
  2470.  */
  2471. PROT_PhishingWarden.prototype.maybeLocateProblem_ = function(request) {
  2472.   G_Debug(this, "Trying to find the problem.");
  2473.  
  2474.   G_Debug(this, this.browserViews_.length + " browser views to check.");
  2475.   for (var i = 0; i < this.browserViews_.length; i++) {
  2476.     if (this.browserViews_[i].tryToHandleProblemRequest(this, request)) {
  2477.       G_Debug(this, "Found browser view willing to handle problem!");
  2478.       return true;
  2479.     }
  2480.     G_Debug(this, "wrong browser view");
  2481.   }
  2482.   return false;
  2483. }
  2484.  
  2485. /**
  2486.  * Indicates if this URL is one of the possible blacklist test URLs.
  2487.  * These test URLs should always be considered as phishy.
  2488.  *
  2489.  * @param url URL to check 
  2490.  * @return A boolean indicating whether this is one of our blacklist
  2491.  *         test URLs
  2492.  */
  2493. PROT_PhishingWarden.prototype.isBlacklistTestURL = function(url) {
  2494.   // Explicitly check for URL so we don't get JS warnings in strict mode.
  2495.   if (kTestUrls[url])
  2496.     return true;
  2497.   return false;
  2498. }
  2499.  
  2500. /**
  2501.  * Callback for found local blacklist match.  First we report that we have
  2502.  * a blacklist hit, then we bring up the warning dialog.
  2503.  * @param status Number enum from callback (PROT_ListWarden.IN_BLACKLIST,
  2504.  *    PROT_ListWarden.IN_WHITELIST, PROT_ListWarden.NOT_FOUND)
  2505.  */
  2506. PROT_PhishingWarden.prototype.localListMatch_ = function(url, request, status) {
  2507.   if (PROT_ListWarden.IN_BLACKLIST != status)
  2508.     return;
  2509.  
  2510.   // Maybe send a report
  2511.   (new PROT_Reporter).report("phishblhit", url);
  2512.   this.houstonWeHaveAProblem_(request);
  2513. }
  2514.  
  2515. /**
  2516.  * Examine data fetched from a lookup server for evidence of a
  2517.  * phishing problem. 
  2518.  *
  2519.  * @param callback Function to invoke if there is a problem. 
  2520.  * @param trValues Object containing name/value pairs the server returned
  2521.  */
  2522. PROT_PhishingWarden.prototype.checkRemoteData = function(callback, 
  2523.                                                          trValues) {
  2524.  
  2525.   if (!trValues) {
  2526.     G_Debug(this, "Didn't get TR values from the server.");
  2527.     return;
  2528.   }
  2529.   
  2530.   G_Debug(this, "Page has phishiness " + trValues["phishy"]);
  2531.  
  2532.   if (trValues["phishy"] == 1) {     // It's on our blacklist 
  2533.     G_Debug(this, "Remote blacklist hit");
  2534.     callback(this);
  2535.   } else {
  2536.     G_Debug(this, "Remote blacklist miss");
  2537.   }
  2538. }
  2539.  
  2540. //@line 37 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\malware-warden.js"
  2541.  
  2542. // This warden manages updates to the malware list
  2543.  
  2544. const kMalwareWardenEnabledPref = "browser.safebrowsing.malware.enabled";
  2545.  
  2546. function PROT_MalwareWarden() {
  2547.   PROT_ListWarden.call(this);
  2548.  
  2549.   this.debugZone = "malwarewarden";
  2550.  
  2551.   // Use this to query preferences
  2552.   this.prefs_ = new G_Preferences();
  2553.  
  2554.   // Global preference to enable the malware warden
  2555.   this.malwareWardenEnabled_ =
  2556.     this.prefs_.getPref(kMalwareWardenEnabledPref, null);
  2557.  
  2558.   // Get notifications when the malware warden enabled pref changes
  2559.   var malwareWardenPrefObserver =
  2560.     BindToObject(this.onMalwareWardenEnabledPrefChanged, this);
  2561.   this.prefs_.addObserver(kMalwareWardenEnabledPref, malwareWardenPrefObserver);
  2562.  
  2563.   // Add a test chunk to the database
  2564.   var testData = "mozilla.com/firefox/its-an-attack.html";
  2565.  
  2566.   var testUpdate =
  2567.     "n:1000\ni:test-malware-simple\nad:1\n" +
  2568.     "a:1:" + testData.length + "\n" +
  2569.     testData +
  2570.     "\n";
  2571.     
  2572.   testData = "mozilla.com/firefox/its-a-trap.html";
  2573.   testUpdate +=
  2574.     "n:1000\ni:test-phish-simple\nad:1\n" +
  2575.     "a:1:" + testData.length + "\n" +
  2576.     testData +
  2577.     "\n";
  2578.  
  2579.   var dbService_ = Cc["@mozilla.org/url-classifier/dbservice;1"]
  2580.                    .getService(Ci.nsIUrlClassifierDBService);
  2581.  
  2582.   dbService_.update(testUpdate);
  2583.   dbService_.finish(function(result) {}, function(error) {});
  2584.  
  2585.   G_Debug(this, "malwareWarden initialized");
  2586. }
  2587.  
  2588. PROT_MalwareWarden.inherits(PROT_ListWarden);
  2589.  
  2590. /**
  2591.  * Cleanup on shutdown.
  2592.  */
  2593. PROT_MalwareWarden.prototype.shutdown = function() {
  2594.   this.prefs_.removeAllObservers();
  2595.  
  2596.   this.listManager_ = null;
  2597. }
  2598.  
  2599. /**
  2600.  * When a preference changes, we might have to start or stop asking for
  2601.  * updates.
  2602.  */
  2603. PROT_MalwareWarden.prototype.maybeToggleUpdateChecking = function() {
  2604.   var malwareWardenEnabled = this.prefs_.getPref(kMalwareWardenEnabledPref,
  2605.                                                  null);
  2606.  
  2607.   G_Debug(this, "Maybe toggling update checking. " +
  2608.           "Warden enabled? " + malwareWardenEnabled);
  2609.  
  2610.   // Do nothing unless thre pref is set
  2611.   if (malwareWardenEnabled === null)
  2612.     return;
  2613.  
  2614.   // We update and save to disk all tables if we don't have remote checking
  2615.   // enabled.
  2616.   if (malwareWardenEnabled === true) {
  2617.     this.enableBlacklistTableUpdates();
  2618.   } else {
  2619.     // Anti-malware is off, disable table updates
  2620.     this.disableBlacklistTableUpdates();
  2621.   }
  2622. }
  2623.  
  2624. /**
  2625.  * Deal with a user changing the pref that says whether we should 
  2626.  * enable the malware warden.
  2627.  *
  2628.  * @param prefName Name of the pref holding the value indicating whether
  2629.  *                 we should enable the malware warden
  2630.  */
  2631. PROT_MalwareWarden.prototype.onMalwareWardenEnabledPrefChanged = function(
  2632.                                                                     prefName) {
  2633.   // Just to be safe, ignore changes to sub prefs.
  2634.   if (prefName != kMalwareWardenEnabledPref)
  2635.     return;
  2636.  
  2637.   this.malwareWardenEnabled_ =
  2638.     this.prefs_.getPref(prefName, this.malwareWardenEnabled_);
  2639.   this.maybeToggleUpdateChecking();
  2640. }
  2641. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\reporter.js"
  2642.  
  2643.  
  2644. // A tiny class to do reporting for us. We report interesting user actions
  2645. // such as the user hitting a blacklisted page, and the user accepting
  2646. // or declining the warning.
  2647. //
  2648. // Each report has a subject and data. Current reports are:
  2649. //
  2650. // subject         data     meaning
  2651. // --------------------------------
  2652. // phishnavaway    url      the user navigated away from a phishy page
  2653. // phishdecline    url      the user declined our warning
  2654. // phishaccept     url      the user accepted our warning
  2655. // phishblhit      url      the user loaded a phishing page
  2656. //
  2657. // We only send reports in advanced protection mode, and even then we
  2658. // strip cookies from the request before sending it.
  2659.  
  2660. /**
  2661.  * A very complicated class to send pings to the provider. The class does
  2662.  * nothing if we're not in advanced protection mode.
  2663.  *
  2664.  * @constructor
  2665.  */
  2666. function PROT_Reporter() {
  2667.   this.debugZone = "reporter";
  2668.   this.prefs_ = new G_Preferences();
  2669. }
  2670.  
  2671. /**
  2672.  * Send a report!
  2673.  *
  2674.  * @param subject String indicating what this report is about (will be 
  2675.  *                urlencoded)
  2676.  * @param data String giving extra information about this report (will be 
  2677.  *                urlencoded)
  2678.  */
  2679. PROT_Reporter.prototype.report = function(subject, data) {
  2680.   // Send a report iff we're in advanced protection mode
  2681.   if (!this.prefs_.getPref(kPhishWardenRemoteLookups, false))
  2682.     return;
  2683.   // Make sure a report url is defined
  2684.   var url = gDataProvider.getReportURL();
  2685.  
  2686.   // Report url is optional, so we just ignore the request if a report
  2687.   // url isn't provided.
  2688.   if (!url)
  2689.     return;
  2690.  
  2691.   url += "evts=" + encodeURIComponent(subject)
  2692.          + "&evtd=" + encodeURIComponent(data);
  2693.   G_Debug(this, "Sending report: " + url);
  2694.   (new PROT_XMLFetcher(true /* strip cookies */)).get(url, null /* no cb */);
  2695. }
  2696. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\content\tr-fetcher.js"
  2697.  
  2698.  
  2699. // A helper class that does "trustrank" lookups on a remote
  2700. // server. Right now this lookup just indicates if a page is
  2701. // phishing. The response format is protocol4 name/value pairs.
  2702. // 
  2703. // Since we're sending full URLs to the server, we try to encrypt
  2704. // them before transmission. Else HTTPS query params could leak.
  2705.  
  2706. /**
  2707.  * A helper class that fetches trustrank values, parses them, and
  2708.  * passes them via an object to a callback.
  2709.   * 
  2710.  * @constructor
  2711.  */
  2712. function PROT_TRFetcher(opt_noCrypto) {
  2713.   this.debugZone = "trfetcher";
  2714.   this.useCrypto_ = !opt_noCrypto;
  2715.   this.protocol4Parser_ = new G_Protocol4Parser();
  2716.  
  2717.   // We lazily instantiate the UrlCrypto object due to:
  2718.   // https://bugzilla.mozilla.org/show_bug.cgi?id=321024
  2719.   //
  2720.   // Otherwise here we would use:
  2721.   // this.urlCrypto_ = new PROT_UrlCrypto();
  2722. }
  2723.  
  2724. PROT_TRFetcher.TRY_REKEYING_RESPONSE = "pleaserekey";
  2725.  
  2726. /**
  2727.  * Get the URL of the request that will fetch us TR for the argument URL
  2728.  *
  2729.  * @param url String containing the URL we'd like to fetch info about
  2730.  *
  2731.  * @returns String containing the url we should use to fetch tr info
  2732.  */
  2733. PROT_TRFetcher.prototype.getRequestURL_ = function(url) {
  2734.  
  2735.   if (!this.urlCrypto_)
  2736.     this.urlCrypto_ = new PROT_UrlCrypto();
  2737.  
  2738.   G_Debug(this, "Fetching for " + url);
  2739.     
  2740.   var requestURL = gDataProvider.getLookupURL();
  2741.   if (!requestURL)
  2742.     return null;
  2743.  
  2744.   if (this.useCrypto_) {
  2745.     var maybeCryptedParams = this.urlCrypto_.maybeCryptParams({ "q": url});
  2746.     
  2747.     for (var param in maybeCryptedParams) 
  2748.       requestURL += param + "=" + 
  2749.                     encodeURIComponent(maybeCryptedParams[param]) + "&";
  2750.   } else {
  2751.     requestURL += "q=" + encodeURIComponent(url);
  2752.   }
  2753.  
  2754.   G_Debug(this, "Request URL: " + requestURL);
  2755.  
  2756.   return requestURL;
  2757. };
  2758.  
  2759. /**
  2760.  * Fetches information about a page.
  2761.  * 
  2762.  * @param forPage URL for which to fetch info
  2763.  *
  2764.  * @param callback Function to call back when complete.
  2765.  */
  2766. PROT_TRFetcher.prototype.get = function(forPage, callback) {
  2767.   
  2768.   var url = this.getRequestURL_(forPage);
  2769.   if (!url) {
  2770.     G_Debug(this, "No remote lookup url.");
  2771.     return;
  2772.   }
  2773.   var closure = BindToObject(this.onFetchComplete_, this, callback);
  2774.   (new PROT_XMLFetcher()).get(url, closure);
  2775. };
  2776.  
  2777. /**
  2778.  * Invoked when a fetch has completed.
  2779.  *
  2780.  * @param callback Function to invoke with parsed response object
  2781.  * @param responseText Text of the protocol4 message
  2782.  * @param httpStatus Number HTTP status code or NS_ERROR_NOT_AVAILABLE if the
  2783.  *     request failed
  2784.  */
  2785. PROT_TRFetcher.prototype.onFetchComplete_ = function(callback, responseText,
  2786.                                                      httpStatus) {
  2787.   
  2788.   var responseObj = this.extractResponse_(responseText);
  2789.  
  2790.   // The server might tell us to rekey, for example if it sees that
  2791.   // our request was unencrypted (meaning that we might not yet have
  2792.   // a key). If so, pass this hint along to the crypto key manager.
  2793.  
  2794.   if (responseObj[PROT_TRFetcher.TRY_REKEYING_RESPONSE] == "1" &&
  2795.       this.urlCrypto_) {
  2796.     G_Debug(this, "We're supposed to re-key. Trying.");
  2797.     var manager = this.urlCrypto_.getManager();
  2798.     if (manager)
  2799.       manager.maybeReKey();
  2800.   }
  2801.  
  2802.   G_Debug(this, "TR Response:");
  2803.   for (var field in responseObj)
  2804.     G_Debug(this, field + "=" + responseObj[field]);
  2805.  
  2806.   callback(responseObj, httpStatus);
  2807. };
  2808.  
  2809. /**
  2810.  * Parse a protocol4 message (lookup server response)
  2811.  * 
  2812.  * @param responseText String containing the server's response
  2813.  *
  2814.  * @returns Object containing the returned values or null if no
  2815.  *          response was received
  2816.  */
  2817. PROT_TRFetcher.prototype.extractResponse_ = function(responseText) {
  2818.   return this.protocol4Parser_.parse(responseText);
  2819. };
  2820.  
  2821. //@line 24 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\safebrowsing\src\nsSafebrowsingApplication.js"
  2822.  
  2823. var modScope = this;
  2824. function Init() {
  2825.   var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
  2826.               .getService().wrappedJSObject;
  2827.   modScope.G_Debug = jslib.G_Debug;
  2828.   modScope.G_Assert = jslib.G_Assert;
  2829.   modScope.G_Alarm = jslib.G_Alarm;
  2830.   modScope.G_ConditionalAlarm = jslib.G_ConditionalAlarm;
  2831.   modScope.G_ObserverWrapper = jslib.G_ObserverWrapper;
  2832.   modScope.G_Preferences = jslib.G_Preferences;
  2833.   modScope.PROT_XMLFetcher = jslib.PROT_XMLFetcher;
  2834.   modScope.BindToObject = jslib.BindToObject;
  2835.   modScope.G_Protocol4Parser = jslib.G_Protocol4Parser;
  2836.   modScope.PROT_UrlCrypto = jslib.PROT_UrlCrypto;
  2837.   modScope.RequestBackoff = jslib.RequestBackoff;
  2838.   
  2839.   // We only need to call Init once
  2840.   modScope.Init = function() {};
  2841. }
  2842.  
  2843. // Module object
  2844. function SafebrowsingApplicationMod() {
  2845.   this.firstTime = true;
  2846.   this.cid = Components.ID("{c64d0bcb-8270-4ca7-a0b3-3380c8ffecb5}");
  2847.   this.progid = "@mozilla.org/safebrowsing/application;1";
  2848. }
  2849.  
  2850. SafebrowsingApplicationMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
  2851.   if (this.firstTime) {
  2852.     this.firstTime = false;
  2853.     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  2854.   }
  2855.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  2856.   compMgr.registerFactoryLocation(this.cid,
  2857.                                   "Safebrowsing Application Module",
  2858.                                   this.progid,
  2859.                                   fileSpec,
  2860.                                   loc,
  2861.                                   type);
  2862.   
  2863.   compMgr.registerFactoryLocation(this.cid,
  2864.                                   "UrlClassifier Blocked Error Page",
  2865.                                   "@mozilla.org/network/protocol/about;1?what=blocked",
  2866.                                   fileSpec,
  2867.                                   loc,
  2868.                                   type);
  2869. };
  2870.  
  2871. SafebrowsingApplicationMod.prototype.getClassObject = function(compMgr, cid, iid) {  
  2872.   if (!cid.equals(this.cid))
  2873.     throw Components.results.NS_ERROR_NO_INTERFACE;
  2874.   if (!iid.equals(Ci.nsIFactory))
  2875.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  2876.  
  2877.   return this.factory;
  2878. }
  2879.  
  2880. SafebrowsingApplicationMod.prototype.canUnload = function(compMgr) {
  2881.   return true;
  2882. }
  2883.  
  2884. SafebrowsingApplicationMod.prototype.factory = {
  2885.   createInstance: function(outer, iid) {
  2886.     if (outer != null)
  2887.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  2888.     Init();
  2889.     return new PROT_Application();
  2890.   }
  2891. };
  2892.  
  2893. var ApplicationModInst = new SafebrowsingApplicationMod();
  2894.  
  2895. function NSGetModule(compMgr, fileSpec) {
  2896.   return ApplicationModInst;
  2897. }
  2898.